Summary

Exploring associations of miRNA expression levels across tumor types in TCGA with suggested correlates defined by the Immune Response Working Group:

  • Overall Leukocyte fraction
  • Individual Relative CIBERSORT fraction
  • Mutation Load
  • TCR,BCR Diversity
  • Expression of
    • IFNG,IFN-gamma
    • PRF1,Perforin
    • GZMA,Granzyme A
    • PDCD1,PD-1
    • CD274,PD-L1
    • PDCD1LG2,PD-L2
    • IL10,IL-10
    • TGFB1,TGF-beta
    • IDO1,IDO
    • HLA-A

 

Prepare data

Retrieve miRNA data from Synapse

Data are stored on Synapse in the folder syn6171109.

Load miRNA sample data

Sample characteristics are stored in a tab-delimited text file (Synapse ID: syn7222010) and can be loaded with read_tsv().

# load sample data
mirna_sample_file <- mirna_files %>% 
    filter(file.id == "syn7222010") %>% 
    .[["file_path"]]
mirna_sample_df <- read_tsv(mirna_sample_file)
Parsed with column specification:
cols(
  id = col_character(),
  Disease = col_character(),
  Sample_Type = col_integer(),
  Protocol = col_character(),
  Platform = col_character()
)

Sample filtering

Sample Quality Annotations: syn4551248 (“merged_sample_quality_annotations.tsv”)

sample_qual_file <- synGet("syn4551248", downloadLocation = "../data/tcga/")
sample_qual_df <- sample_qual_file %>% 
    getFileLocation() %>% 
    read_tsv()
Parsed with column specification:
cols(
  patient_barcode = col_character(),
  aliquot_barcode = col_character(),
  `cancer type` = col_character(),
  platform = col_character(),
  patient_annotation = col_character(),
  sample_annotation = col_character(),
  aliquot_annotation = col_character(),
  aliquot_annotation_updated = col_character(),
  AWG_excluded_because_of_pathology = col_double(),
  AWG_pathology_exclusion_reason = col_character(),
  Reviewed_by_EPC = col_double(),
  Do_not_use = col_character()
)

remove samples based on Do_not_use=True, and remove cases with AWG_excluded_because_of_pathology=True

# samples to exclude from all datasets
exclude_samples <- sample_qual_df %>% 
    mutate(AWG_excluded_because_of_pathology = parse_logical(AWG_excluded_because_of_pathology),
           Do_not_use = parse_logical(str_to_upper(Do_not_use))) %>% 
    filter(AWG_excluded_because_of_pathology | Do_not_use)

Remove samples from miRNA dataset.

mirna_sample_df <- mirna_sample_df %>% 
    filter(!(id %in% exclude_samples$aliquot_barcode))

 

Load miRNA expression data

miRNA normalized, batch corrected expression values for all samples are stored as a matrix in a CSV file (Synapse ID: syn7201053) and can be loaded with read_csv().

mirna_corr_file <- "../data/intermediate/mirna_correlate_data.feather"
force_read <- FALSE
if (!file.exists(mirna_corr_file) | force_read) {
    # load normalized, batch-corrected expression data
    mirna_norm_file <- mirna_files %>% 
        filter(file.id == "syn7201053") %>% 
        .[["file_path"]]
    mirna_norm_df <- read_csv(mirna_norm_file, progress = FALSE)
    
    mirna_corr_df <- mirna_norm_df %>% 
        select(one_of(c("Genes", mirna_sample_pt_df$id)))
    
    write_feather(mirna_corr_df, mirna_corr_file)
    rm(mirna_norm_df)
} else {
    mirna_corr_df <- read_feather(mirna_corr_file)
}

 

Set up plotting colors

tcga_colors <- tribble(
    ~Color, ~Disease,
    "#ED2891", "BRCA",
    "#B2509E", "GBM",
    "#D49DC7", "LGG",
    "#C1A72F", "ACC",
    "#E8C51D", "PCPG",
    "#F9ED32", "THCA",
    "#104A7F", "CHOL",
    "#9EDDF9", "COAD",
    "#007EB5", "ESCA",
    "#CACCDB", "LIHC",
    "#6E7BA2", "PAAD",
    "#DAF1FC", "READ",
    "#00AEEF", "STAD",
    "#F6B667", "CESC",
    "#D97D25", "OV",
    "#FBE3C7", "UCEC",
    "#F89420", "UCS",
    "#97D1A9", "HNSC",
    "#009444", "UVM",
    "#754C29", "LAML",
    "#CEAC8F", "THYM",
    "#3953A4", "DLBC",
    "#BBD642", "SKCM",
    "#00A99D", "SARC",
    "#D3C3E0", "LUAD",
    "#A084BD", "LUSC",
    "#542C88", "MESO",
    "#FAD2D9", "BLCA",
    "#ED1C24", "KICH",
    "#F8AFB3", "KIRC",
    "#EA7075", "KIRP",
    "#7E1918", "PRAD",
    "#BE1E2D", "TGCT"
)
mirna_sample_df <- mirna_sample_df %>% 
    mutate(Disease = factor(Disease, levels = tcga_colors$Disease))

 

Explore miRNA data

Sample characteristics

The table mirna_sample_df contains 5 columns describing the 10543 samples in the data. The Sample_Type column corresponds to TCGA sample type codes, which are defined here. The following sample types are included in the miRNA data:

mirna_sample_df %>% 
    group_by(Sample_Type) %>% 
    tally()

 

Based on the codes, this is the distribution of sample types:

mirna_sample_df %>% 
    group_by(Sample_Type) %>% 
    tally() %>% 
    mutate(Definition = case_when(
        .$Sample_Type == 1 ~ "Primary Solid Tumor",
        .$Sample_Type == 2 ~ "Recurrent Solid Tumorr",
        .$Sample_Type == 3 ~ "Primary Blood Derived Cancer - Peripheral Blood",
        .$Sample_Type == 5 ~ "Additional - New Primary",
        .$Sample_Type == 6 ~ "Metastatic",
        .$Sample_Type == 7 ~ "Additional Metastatic",
        .$Sample_Type == 11 ~ "Solid Tissue Normal"
    ))

Note: only include “primary” tumor samples for now; also, add additional column to store vial ID (to ease mapping between data sets).

mirna_sample_pt_df <- mirna_sample_df %>% 
    filter(Sample_Type %in% c(1, 3, 5)) %>% 
    mutate(vial_id = str_replace(id, "(\\-[:alnum:]+){3}$", ""))

 

Additionally, the following disease types are included:

mirna_sample_pt_df %>% 
    group_by(Disease) %>% 
    tally()

Note: exclude LAML, THYM, and DLBC from cell content correlations

 

And technical variables:

mirna_sample_pt_df %>% 
    group_by(Protocol, Platform) %>% 
    tally()

 

miRNA expression values

Expression values in the data are reportedly reads per million (RPM). I’ve randomly selected a few samples from each disease type, sample type, protocol, and platform to inspect the distribution of expression values across all 743 miRNA genes.

set.seed(0)
# randomly select 1-3 samples from each combination of characteristics
sample_sub_df <- mirna_sample_pt_df %>% 
    group_by(Disease, Sample_Type, Protocol, Platform) %>% 
    sample_n(3, replace = TRUE) %>% 
    ungroup() %>% 
    distinct()
# subset and melt the expression data
mirna_sub_df <- mirna_corr_df %>% 
    select(one_of(c("Genes", sample_sub_df$id))) %>%
    gather("sample", "expression", -Genes)

 

Even with a shifted log (log10(x + 1)) transformation, expression values look to be more exponentially distributed than normal.

mirna_sub_df %>% 
    left_join(mirna_sample_pt_df, by = c("sample" = "id")) %>% 
    ggplot(aes(x = log10(expression + 1))) +
    stat_density(aes(group = sample), geom = "line", position = "identity", 
                 alpha = 0.2)

 

I can also look at the distribution of log-RPM values with boxplots:

mirna_sub_df %>% 
    left_join(mirna_sample_pt_df, by = c("sample" = "id")) %>% 
    ggplot(aes(x = sample, y = log10(expression + 1))) +
    geom_boxplot(aes(fill = Disease), outlier.size = 0.5) +
    theme(axis.text.x = element_blank()) +
    scale_fill_manual(values = tcga_colors$Color)

# convert expression df to matrix
mirna_corr_mat <- mirna_corr_df %>% 
    select(one_of(c("Genes", mirna_sample_pt_df$id))) %>% 
    column_to_rownames("Genes") %>% 
    as.matrix()

 

PCA

I used the prcomp() function on the transposed expression matrix (samples x genes, after transposing) to compute PCA data, which I can use to look for batch effects among samples.

mirna_corr_pca <- mirna_corr_mat %>% 
    t() %>% 
    prcomp()

(I can use the broom:tidy() function to convert data in the prcomp object into data frames for ggplot.)

pc_df <- mirna_corr_pca %>% 
    tidy("pcs")
mirna_corr_pca_df <- mirna_corr_pca %>% 
    tidy("samples") %>% 
    filter(PC <= 2) %>% 
    left_join(mirna_sample_pt_df, by = c("row" = "id"))
rm(mirna_corr_mat)

The plot below shows samples plotted as points along the first two principle components (PCs). Points are colored by disease type.

pc1_label <- pc_df %>% 
    filter(PC == 1) %>% 
    transmute(label = sprintf("PC%s [%0.2f%%]", PC, 100*percent)) %>% 
    flatten_chr()
pc2_label <- pc_df %>% 
    filter(PC == 2) %>% 
    transmute(label = sprintf("PC%s [%0.2f%%]", PC, 100*percent)) %>% 
    flatten_chr()
mirna_corr_pca_df %>% 
    spread(PC, value) %>% 
    ggplot(aes(x = `1`, y = `2`)) +
    geom_point(aes(colour = Disease)) +
    xlab(pc1_label) +
    ylab(pc2_label) +
    scale_color_manual(values = tcga_colors$Color)


 

Prepare correlate data

  • Overall Leukocyte fraction
  • Individual Relative CIBERSORT fraction
  • Mutation Load
  • TCR,BCR Diversity
  • Gene Expression

Leukocyte fraction

Retrieve/load data

Cellular Content: syn7994728 syn5808205 (“TCGA_all_leuk_estimate.masked.20170107.tsv”)

# file_data <- synGet("syn5808205", downloadLocation = "./")
leuk_frac_file <- "../data/tcga/TCGA_all_leuk_estimate.masked.20170107.tsv"
leuk_frac_df <- read_tsv(leuk_frac_file, col_names = FALSE) %>% 
    set_names(c("disease", "id", "leuk_frac"))
Parsed with column specification:
cols(
  X1 = col_character(),
  X2 = col_character(),
  X3 = col_double()
)

Sample filtering

leuk_frac_df <- leuk_frac_df %>%
    filter(!(id %in% exclude_samples$aliquot_barcode))

Sample matching

Identify matched samples between miRNA and leukocyte fraction.

leuk_frac_ids <- leuk_frac_df %>% 
    select(id) %>%
    mutate(vial_id = str_replace(id, "(\\-[:alnum:]+){3}$", "")) %>% 
    arrange()
mirna_ids <- mirna_sample_pt_df %>%
    select(id, vial_id) %>%
    arrange()
mirna_leuk_frac_shared_ids <- inner_join(mirna_ids, leuk_frac_ids, 
                                         by = "vial_id",
                                         suffix = c("_mirna", "_leuk_frac"))
# only keep samples with matched vial ID AND portion number
portion_id_minus_analyte_regex <- "([:alnum:]+\\-){4}[0-9]+"
# plate_id_only_regex <- "[:alnum:]+(?=([:alnum:]\\-[:alnum:]+){1}$)"
mirna_leuk_frac_shared_ids <- mirna_leuk_frac_shared_ids %>%
    filter((str_extract(id_mirna, portion_id_minus_analyte_regex)
            == str_extract(id_leuk_frac, portion_id_minus_analyte_regex)))

NOTE: several samples were assayed on multiple plates; average the leukocyte fraction across these before computing correlations with miRNA

Correlate data formatting

leuk_frac_corr_file <- "../data/intermediate/leuk_frac_correlates_for_mirna.feather"
force_format <- FALSE
if (!file.exists(leuk_frac_corr_file) | force_format) {
    leuk_frac_corr_df <- mirna_leuk_frac_shared_ids %>% 
        left_join(leuk_frac_df, by = c("id_leuk_frac" = "id")) %>% 
        group_by(disease, vial_id) %>% 
        summarise(leuk_frac = mean(leuk_frac)) %>% 
        ungroup()
    
    write_feather(leuk_frac_corr_df, leuk_frac_corr_file)
} else {
    leuk_frac_corr_df <- read_feather(leuk_frac_corr_file)
}

Disease-wise correlations

mirna_leuk_frac_corr_file <- "../results/mirna_leuk_frac_correlation.feather"
force_compute <- FALSE
if (!file.exists(mirna_leuk_frac_corr_file) | force_compute) {
    
    # skip immune cell cancers?
    d_list <- mirna_sample_pt_df %>% 
        # filter((!Disease %in% c("LAML", "THYM", "DLBC"))) %>% 
        filter(Disease %in% leuk_frac_corr_df$disease) %>% 
        distinct(Disease) %>% 
        mutate(Disease = as.character(Disease)) %>% 
        .$Disease %>%
        set_names(.) %>% 
        as.list()
    
    corr_df_list <- mclapply(d_list, mc.cores = 4, function(d) {
        samples_d <- mirna_sample_pt_df %>%
            filter(Disease == d) %>%
            select(vial_id) %>%
            flatten_chr() %>%
            intersect(leuk_frac_corr_df$vial_id)
        source_df <- mirna_corr_df %>%
            set_names(str_replace(names(.), "(\\-[:alnum:]+){3}$", "")) %>%
            select(one_of(c("Genes", samples_d))) %>%
            dplyr::rename(mirna = Genes) %>%
            gather(sample, x, -mirna) %>% 
            filter(!is.na(x))
        
        target_df <- leuk_frac_corr_df %>%
            filter(vial_id %in% samples_d) %>% 
            dplyr::rename(sample = vial_id, x = leuk_frac) %>% 
            mutate(correlate = "leuk_frac")
        
        corr_df <- inner_join(source_df, target_df,
                              by = "sample",
                              suffix = c("_source", "_target")) %>%
            group_by(mirna, correlate) %>%
            do(tidy(cor.test(.$x_source, .$x_target, method = "spearman"))) %>% 
            ungroup()
        corr_df[["p.adjust"]] <- p.adjust(corr_df$p.value, method = "BH")
        return(corr_df)
    })
    
    mirna_leuk_frac_corr_df <- bind_rows(corr_df_list, .id = "disease") %>% 
        mutate(correlate_type = "leukocyte fraction")
    write_feather(mirna_leuk_frac_corr_df, mirna_leuk_frac_corr_file)
} else {
    mirna_leuk_frac_corr_df <- read_feather(mirna_leuk_frac_corr_file)
}

 

CIBERSORT fraction

Retrieve/load data

Cellular Content: syn4991611 syn8024565 (“TCGA.cluster-by-CIBERSORT-relative.tsv”)

or…?

syn7337221 (“TCGA.Kallisto.fullIDs.cibersort.relative.tsv”)

# ciber_frac_file <- "../data/tcga/TCGA.cluster-by-CIBERSORT-relative.tsv"
ciber_frac_file <- "../data/tcga/TCGA.Kallisto.fullIDs.cibersort.relative.tsv"
ciber_frac_df <- read_tsv(ciber_frac_file)
Parsed with column specification:
cols(
  .default = col_double(),
  SampleID = col_character(),
  CancerType = col_character()
)
See spec(...) for full column specifications.

Sample filtering

ciber_frac_df <- ciber_frac_df %>%
    mutate(id = str_replace_all(SampleID, "\\.", "\\-")) %>% 
    filter(!(id %in% exclude_samples$aliquot_barcode))

Sample matching

Identify matched samples between miRNA and CIBERSORT fraction.

ciber_frac_ids <- ciber_frac_df %>% 
    select(id) %>%
    mutate(vial_id = str_replace(id, "(\\-[:alnum:]+){3}$", "")) %>% 
    arrange()
# only keep samples with matched vial ID AND portion number
mirna_ciber_frac_shared_ids <- inner_join(mirna_ids, ciber_frac_ids,
                                         by = "vial_id",
                                         suffix = c("_mirna", "_ciber_frac")) %>% 
    distinct() %>% 
    filter((str_extract(id_mirna, portion_id_minus_analyte_regex) 
            == str_extract(id_ciber_frac, portion_id_minus_analyte_regex)))

NOTE: several samples were assayed on multiple plates; average the CIBERSORT fraction across these before computing correlations with miRNA

Correlate data formatting

ciber_frac_corr_file <- "../data/intermediate/ciber_frac_correlates_for_mirna.feather"
force_format <- FALSE
if (!file.exists(ciber_frac_corr_file) | force_format) {
    ciber_frac_corr_df <- mirna_ciber_frac_shared_ids %>%
        left_join(ciber_frac_df, by = c("id_ciber_frac" = "id")) %>% 
        select(-id_mirna, -id_ciber_frac, 
               -SampleID, -P.value, -Correlation, -RMSE) %>% 
        group_by(CancerType, vial_id) %>%
        summarise_each(funs(mean)) %>% 
        ungroup()
    
    write_feather(ciber_frac_corr_df, ciber_frac_corr_file)
} else {
    ciber_frac_corr_df <- read_feather(ciber_frac_corr_file)
}

Disease-wise correlations

mirna_ciber_frac_corr_file <- "../results/mirna_ciber_frac_correlation.feather"
force_compute <- FALSE
if (!file.exists(mirna_ciber_frac_corr_file) | force_compute) {
    
    # skip immune cell cancers?
    d_list <- mirna_sample_pt_df %>%
        # filter((!Disease %in% c("LAML", "THYM", "DLBC"))) %>% 
        filter(Disease %in% ciber_frac_corr_df$CancerType) %>% 
        distinct(Disease) %>% 
        mutate(Disease = as.character(Disease)) %>% 
        .$Disease %>%
        set_names(.) %>% 
        as.list()
    
    corr_df_list <- mclapply(d_list, mc.cores = 4, function(d) {
        samples_d <- mirna_sample_pt_df %>%
            filter(Disease == d) %>%
            select(vial_id) %>%
            flatten_chr() %>%
            intersect(ciber_frac_corr_df$vial_id)
        source_df <- mirna_corr_df %>%
            set_names(str_replace(names(.), "(\\-[:alnum:]+){3}$", "")) %>%
            select(one_of(c("Genes", samples_d))) %>%
            dplyr::rename(mirna = Genes) %>%
            gather(sample, x, -mirna) %>% 
            filter(!is.na(x))
        target_df <- ciber_frac_corr_df %>%
            filter(vial_id %in% samples_d) %>% 
            select(-CancerType) %>% 
            gather(correlate, x, -vial_id) %>% 
            dplyr::rename(sample = vial_id)
        
        corr_df <- inner_join(source_df, target_df,
                              by = "sample",
                              suffix = c("_source", "_target")) %>%
            group_by(mirna, correlate) %>%
            do(tidy(cor.test(.$x_source, .$x_target, method = "spearman"))) %>% 
            ungroup()
        corr_df[["p.adjust"]] <- p.adjust(corr_df$p.value, method = "BH")
        return(corr_df)
    })
    
    mirna_ciber_frac_corr_df <- bind_rows(corr_df_list, .id = "disease") %>% 
        mutate(correlate_type = "CIBERSORT fraction")
    write_feather(mirna_ciber_frac_corr_df, mirna_ciber_frac_corr_file)
} else {
    mirna_ciber_frac_corr_df <- read_feather(mirna_ciber_frac_corr_file)
}

 

Mutation load

Retrieve/load data

Mutation Load: syn5706827 syn7994728 (“mutation-load”)

  • exclude LAML?
mut_load_file <- "../data/tcga/mutation-load-vial.txt"
mut_load_df <- read_tsv(mut_load_file)
Parsed with column specification:
cols(
  cohort = col_character(),
  Patient_ID = col_character(),
  Tumor_Sample_ID = col_character(),
  Tumor_Sample_ID_Vial = col_character(),
  `Silent per Mb` = col_double(),
  `Non-silent per Mb` = col_double()
)

Sample filtering

Can’t filter aliquots, as data only includes IDs specific to the level of vial

Sample matching

Identify matched samples between miRNA and mutational load.

mut_load_ids <- mut_load_df %>% 
    select(id = Tumor_Sample_ID_Vial) %>% 
    mutate(vial_id = id) %>%
    arrange()
mirna_mut_load_shared_ids <- inner_join(mirna_ids, mut_load_ids, 
                                        by = "vial_id",
                                        suffix = c("_mirna", "_mut_load"))

Correlate data formatting

mut_load_corr_file <- "../data/intermediate/mut_load_correlates_for_mirna.feather"
force_format <- FALSE
if (!file.exists(mut_load_corr_file) | force_format) {
    mut_load_corr_df <- mirna_mut_load_shared_ids %>% 
        left_join(mut_load_df, by = c("id_mut_load" = "Tumor_Sample_ID_Vial"))
    
    write_feather(mut_load_corr_df, mut_load_corr_file)
} else {
    mut_load_corr_df <- read_feather(mut_load_corr_file)
}

Disease-wise correlations

mirna_mut_load_corr_file <- "../results/mirna_mut_load_correlation.feather"
force_compute <- FALSE
if (!file.exists(mirna_mut_load_corr_file) | force_compute) {
    
    # skip immune cell cancers?
    d_list <- mirna_sample_pt_df %>%
        # filter((!Disease %in% c("LAML", "THYM", "DLBC"))) %>%
        filter(Disease %in% mut_load_corr_df$cohort) %>% 
        distinct(Disease) %>% 
        mutate(Disease = as.character(Disease)) %>% 
        .$Disease %>%
        set_names(.) %>% 
        as.list()
    
    corr_df_list <- mclapply(d_list, mc.cores = 4, function(d) {
        samples_d <- mirna_sample_pt_df %>%
            filter(Disease == d) %>%
            select(vial_id) %>%
            flatten_chr() %>%
            intersect(mut_load_corr_df$vial_id)
        source_df <- mirna_corr_df %>%
            set_names(str_replace(names(.), "(\\-[:alnum:]+){3}$", "")) %>%
            select(one_of(c("Genes", samples_d))) %>%
            dplyr::rename(mirna = Genes) %>%
            gather(sample, x, -mirna) %>% 
            filter(!is.na(x))
        target_df <- mut_load_corr_df %>%
            filter(vial_id %in% samples_d) %>% 
            select(-id_mirna, -id_mut_load, -cohort, -Patient_ID, 
                   -Tumor_Sample_ID) %>% 
            gather(correlate, x, -vial_id) %>% 
            dplyr::rename(sample = vial_id)
        
        corr_df <- inner_join(source_df, target_df,
                              by = "sample",
                              suffix = c("_source", "_target")) %>%
            group_by(mirna, correlate) %>%
            do(tidy(cor.test(.$x_source, .$x_target, method = "spearman"))) %>% 
            ungroup()
        corr_df[["p.adjust"]] <- p.adjust(corr_df$p.value, method = "BH")
        return(corr_df)
    })
    
    mirna_mut_load_corr_df <- bind_rows(corr_df_list, .id = "disease") %>% 
        mutate(correlate_type = "mutation load")
    write_feather(mirna_mut_load_corr_df, mirna_mut_load_corr_file)
} else {
    mirna_mut_load_corr_df <- read_feather(mirna_mut_load_corr_file)
}

 

TCR/BCR diversity

Retrieve/load data

T Cell Receptor / Brown et al: syn5876488 syn7063422 (“mitcr_sampleStatistics_20160714.tsv”)

tcr_div_file <- "../data/tcga/mitcr_sampleStatistics_20160714.tsv"
tcr_div_df <- read_tsv(tcr_div_file)
Parsed with column specification:
cols(
  AliquotBarcode = col_character(),
  Study = col_character(),
  SampleTypeLetterCode = col_character(),
  ParticipantBarcode = col_character(),
  SampleBarcode = col_character(),
  CGHub_analysis_id = col_character(),
  totTCR_reads = col_integer(),
  totTCRa_reads = col_integer(),
  totTCRb_reads = col_integer(),
  shannon = col_double(),
  numClones = col_integer()
)

Sample filtering

tcr_dv_df <- tcr_div_df %>%
    filter(!(AliquotBarcode %in% exclude_samples$aliquot_barcode))

Sample matching

Identify matched samples between miRNA and CIBERSORT fraction.

tcr_div_ids <- tcr_div_df %>% 
    select(id = AliquotBarcode, vial_id = SampleBarcode) %>% 
    arrange()
# only keep samples with matched vial ID AND portion number
mirna_tcr_div_shared_ids <- inner_join(mirna_ids, tcr_div_ids,
                                       by = "vial_id",
                                       suffix = c("_mirna", "_tcr_div")) %>% 
    distinct() %>% 
    filter((str_extract(id_mirna, portion_id_minus_analyte_regex) 
            == str_extract(id_tcr_div, portion_id_minus_analyte_regex)))

NOTE: several samples were assayed on multiple plates; average the TCR diversity across these before computing correlations with miRNA

Correlate data formatting

Some samples (AliquotBarcode) have 2 values for Shannon entropy, apparently corresponding to separate analyses on CGHub. I’ll go ahead and average these values per aliquot, prior to averaging Shannon values per vial ID. As a small measure of QC, I’ll also only keep observations with at least 1 TCRA read or 1 TCRB read.

tcr_div_corr_file <- "../data/intermediate/tcr_div_correlates_for_mirna.feather"
force_format <- FALSE
if (!file.exists(tcr_div_corr_file) | force_format) {
    tcr_div_corr_df <- mirna_tcr_div_shared_ids %>%
        left_join(tcr_div_df, by = c("id_tcr_div" = "AliquotBarcode")) %>%
        select(-CGHub_analysis_id) %>% 
        distinct() %>% 
        filter(totTCRa_reads > 0 | totTCRb_reads > 0) %>%
        group_by(Study, vial_id, id_tcr_div) %>%
        summarize(shannon = mean(shannon)) %>%
        group_by(Study, vial_id) %>%
        summarise(shannon = mean(shannon)) %>% 
        ungroup()
    write_feather(tcr_div_corr_df, tcr_div_corr_file)
} else {
    tcr_div_corr_df <- read_feather(tcr_div_corr_file)
}

Disease-wise correlations

mirna_tcr_div_corr_file <- "../results/mirna_tcr_div_correlation.feather"
force_compute <- FALSE
if (!file.exists(mirna_tcr_div_corr_file) | force_compute) {
    
    # skip immune cell cancers
    d_list <- mirna_sample_pt_df %>%
        # filter((!Disease %in% c("LAML", "THYM", "DLBC"))) %>% 
        filter(Disease %in% tcr_div_corr_df$Study) %>% 
        distinct(Disease) %>% 
        mutate(Disease = as.character(Disease)) %>% 
        .$Disease %>%
        set_names(.) %>% 
        as.list()
    
    corr_df_list <- mclapply(d_list, mc.cores = 4, function(d) {
        samples_d <- mirna_sample_pt_df %>%
            filter(Disease == d) %>%
            select(vial_id) %>%
            flatten_chr() %>%
            intersect(tcr_div_corr_df$vial_id)
        source_df <- mirna_corr_df %>%
            set_names(str_replace(names(.), "(\\-[:alnum:]+){3}$", "")) %>%
            select(one_of(c("Genes", samples_d))) %>%
            dplyr::rename(mirna = Genes) %>%
            gather(sample, x, -mirna) %>% 
            filter(!is.na(x))
        target_df <- tcr_div_corr_df %>%
            filter(vial_id %in% samples_d) %>% 
            select(-Study) %>% 
            dplyr::rename(sample = vial_id) %>% 
            gather(correlate, x, -sample)
        
        corr_df <- inner_join(source_df, target_df,
                              by = "sample",
                              suffix = c("_source", "_target")) %>%
            group_by(mirna, correlate) %>%
            do(tidy(cor.test(.$x_source, .$x_target, method = "spearman"))) %>% 
            ungroup()
        corr_df[["p.adjust"]] <- p.adjust(corr_df$p.value, method = "BH")
        return(corr_df)
    })
    
    mirna_tcr_div_corr_df <- bind_rows(corr_df_list, .id = "disease") %>% 
        mutate(correlate_type = "TCR diversity")
    write_feather(mirna_tcr_div_corr_df, mirna_tcr_div_corr_file)
} else {
    mirna_tcr_div_corr_df <- read_feather(mirna_tcr_div_corr_file)
}

 

Gene expression

  • Expression of
    • IFNG,IFN-gamma
    • PRF1,Perforin
    • GZMA,Granzyme A
    • PDCD1,PD-1
    • CD274,PD-L1
    • PDCD1LG2,PD-L2
    • IL10,IL-10
    • TGFB1,TGF-beta
    • IDO1,IDO
    • HLA-A

Retrieve/load mRNA data

Batch effects normalized mRNA data: syn4976363 syn4976369 (“EB++AdjustPANCAN_IlluminaHiSeq_RNASeqV2.geneExp.tsv”) syn4976366 (" EB++GeneExpAnnotation.tsv“)

Sample characteristics are stored in a tab-delimited text file (Synapse ID: syn4976366) and can be loaded with read_tsv().

# load sample data
mrna_sample_file <- mrna_files %>% 
    filter(file.id == "syn4976366") %>% 
    .[["file_path"]]
mrna_sample_df <- read_tsv(mrna_sample_file)
Parsed with column specification:
cols(
  SampleID = col_character(),
  Center = col_character(),
  platform = col_character(),
  Adjustment = col_character()
)

Sample filtering

Remove samples from mRNA dataset.

mrna_sample_df <- mrna_sample_df %>% 
    filter(!(SampleID %in% exclude_samples$aliquot_barcode))

Sample matching

Identify matched samples between miRNA and mRNA.

mrna_ids <- mrna_sample_df %>% 
    select(SampleID) %>%
    mutate(vial_id = str_replace(SampleID, "(\\-[:alnum:]+){3}$", "")) %>% 
    arrange()
mirna_mrna_shared_ids <- inner_join(mirna_ids, mrna_ids, by = "vial_id")
# only keep samples with matched vial ID AND portion number
mirna_mrna_shared_ids <- mirna_mrna_shared_ids %>%
    filter(str_extract(id, portion_id_minus_analyte_regex)
           == str_extract(SampleID, portion_id_minus_analyte_regex))

mRNA normalized, batch corrected expression values for all samples are stored as a matrix in a TSV file (Synapse ID: syn4976369) and can be loaded with read_tsv().

Correlate data formatting

List of genes accessed here and saved as a TSV at data/Cancer Immunomodulators - TCGA PanImmune Group - Direct Relationship.tsv:

gene_correlate_file <- "../data/Cancer Immunomodulators - TCGA PanImmune Group - Direct Relationship.tsv"
gene_correlate_df <- read_tsv(gene_correlate_file)
Parsed with column specification:
cols(
  Gene = col_character(),
  `HGNC Symbol` = col_character(),
  `Entrez ID` = col_integer(),
  `Gene Family` = col_character(),
  `Immune Checkpoint` = col_character(),
  matched_name = col_character(),
  group = col_character()
)
3 parsing failures.
row col  expected    actual
 20  -- 7 columns 6 columns
 61  -- 7 columns 6 columns
 64  -- 7 columns 6 columns
mrna_corr_file <- "../data/intermediate/mrna_correlates_for_mirna.feather"
force_format <- FALSE
if (!file.exists(mrna_corr_file) | force_format) {
    # load normalized, batch-corrected expression data
    mrna_norm_file <- mrna_files %>% 
        filter(file.id == "syn4976369") %>% 
        .[["file_path"]]
    mrna_norm_df <- read_tsv(mrna_norm_file, progress = FALSE)
    
    # gene_list <- c("IFNG", "PRF1", "GZMA", "PDCD1", "CD274", "PDCD1LG2", "IL10", 
    #                "TGFB1", "IDO1", "HLA-A")  
    mrna_corr_df <- mrna_norm_df %>% 
        separate(gene_id, c("gene_name", "gene_id"), sep = "\\|") %>% 
        filter((gene_id %in% gene_correlate_df$`Entrez ID`) 
               | (gene_name %in% gene_correlate_df$`HGNC Symbol`)) %>% 
        select(one_of(c("gene_name", "gene_id", 
                        mirna_mrna_shared_ids$SampleID)))
    write_feather(mrna_corr_df, mrna_corr_file)
    rm(mrna_norm_df)
} else {
    mrna_corr_df <- read_feather(mrna_corr_file)
}

Disease-wise correlations

mirna_mrna_corr_file <- "../results/mirna_mrna_correlation.feather"
force_compute <- FALSE
if (!file.exists(mirna_mrna_corr_file) | force_compute) {
    
    d_list <- mirna_sample_pt_df %>%
        distinct(Disease) %>% 
        mutate(Disease = as.character(Disease)) %>% 
        .$Disease %>%
        set_names(.) %>% 
        as.list()
    
    corr_df_list <- mclapply(d_list, mc.cores = 4, function(d) {
        samples_d <- mirna_sample_pt_df %>%
            filter(Disease == d) %>%
            select(vial_id) %>%
            flatten_chr() %>%
            intersect(str_replace(names(mrna_corr_df),
                                  "(\\-[:alnum:]+){3}$", ""))
        source_df <- mirna_corr_df %>%
            set_names(str_replace(names(.), "(\\-[:alnum:]+){3}$", "")) %>%
            select(one_of(c("Genes", samples_d))) %>%
            dplyr::rename(mirna = Genes) %>%
            gather(sample, x, -mirna) %>% 
            filter(!is.na(x))
        
        target_df <- mrna_corr_df %>%
            set_names(str_replace(names(.), "(\\-[:alnum:]+){3}$", "")) %>%
            select(one_of(c("gene_name", samples_d))) %>%
            dplyr::rename(correlate = gene_name) %>%
            gather(sample, x, -correlate) %>% 
            filter(!is.na(x))
        
        corr_df <- inner_join(source_df, target_df,
                              by = "sample",
                              suffix = c("_source", "_target")) %>%
            group_by(mirna, correlate) %>%
            do(tidy(cor.test(.$x_source, .$x_target, method = "spearman"))) %>% 
            ungroup()
        corr_df[["p.adjust"]] <- p.adjust(corr_df$p.value, method = "BH")
        return(corr_df)
    })
    
    mirna_mrna_corr_df <- bind_rows(corr_df_list, .id = "disease") %>% 
        mutate(correlate_type = "mRNA expression")
    write_feather(mirna_mrna_corr_df, mirna_mrna_corr_file)
} else {
    mirna_mrna_corr_df <- read_feather(mirna_mrna_corr_file)
}

 

Summarize results

Add color palette

get_cet_pal <- function(cet_path) {
    read_lines(cet_path) %>%
        as.list() %>%
        map_chr(function(c) {
            c_rgb <- str_split_fixed(string = c, pattern = ",", n = 3)
            rgb(c_rgb[1], c_rgb[2], c_rgb[3], maxColorValue = 255)
        })
}
# here's an example with the diverging blue-to yellow colormap:
cet_path <- "~/Downloads/CETperceptual_csv_0_255/diverging-linear_bjy_30-90_c45_n256.csv"
my_cet_pal <- get_cet_pal(cet_path)

External miRNA data

Regulator miRs

mirna_causal_df <- readxl::read_excel("../data/causal_TCGA_panCancer_miRNA_immuneInfiltrate_3_15_2017.xlsx") %>% 
    .[, 1:9] %>% 
    mutate(mirna_disease = str_c(`miRNA Name`, `Tumor Type`, sep = "_"))

Known immune miRs

mirna_immune_df <- readxl::read_excel("../data/Paladini_immune_miRNAs.xlsx")
mirna_immune_tidy_df <- mirna_immune_df %>%
    mutate(mirna_group = str_split(MicroRNAs, ",")) %>%
    unnest(mirna_group) %>%
    mutate(mirna_group = str_trim(mirna_group, "both")) %>%
    # distinct(mirna_group) %>%
    mutate(mirna = mirna_group) %>%
    # filter(Category %in% c("Innate immunity", "Adaptive immunity")) %>%
    # filter(str_detect(mirna, "cluster")) %>%
    filter(!str_detect(mirna_group, "[0-9]+[a-z]{2,}")) %>% 
    mutate(mirna = str_replace(mirna, "miR-17/92 cluster", "miR-17,miR-18a,miR-19a,miR-20a,miR-19b-1,miR-92a-1"),
           mirna = str_replace(mirna, "miR-212/132 cluster", "miR-212,miR-132"),
           mirna = str_replace(mirna, "miR-10 family", "miR-10a,miR-10b,miR-99a,miR-99b,miR-100,miR-125a,miR-125b-1,miR-125b-2"),
           mirna = str_replace(mirna, "miR-221/222", "miR-221,miR-222"),
           mirna = str_replace(mirna, "miR-10a/b", "miR-10a,miR-10b"),
           mirna = str_replace(mirna, "miR-148/152", "miR-148,miR-152"),
           mirna = str_replace(mirna, "miR-17-5p/20a", "miR-17-5p,miR-20a"),
           mirna = str_replace(mirna, "miR-221/222 cluster", "miR-221,miR-222"),
           mirna = str_replace(mirna, "miR-181a/b", "miR-181a,miR-181b"),
           mirna = str_replace(mirna, "miR-15/16", "miR-15,miR-16"),
           mirna = str_replace(mirna, "miR-181 family", "miR-181a-1,miR-181a-2,miR-181b-1,miR-181b-2,miR-181c,miR-181d"),
           mirna = str_replace(mirna, "miR-130/301", "miR-130,miR-301"),
           mirna = str_replace(mirna, "miR-99a/miR-150", "miR-99a,miR-150"),
           mirna = str_replace(mirna, "Let", "let")) %>%
    mutate(mirna = str_split(mirna, ",")) %>% 
    unnest(mirna) %>% 
    # left_join(mirna_sig_corr_df %>%
    #               filter(correlate_type == "CIBERSORT fraction") %>%
    #               mutate(mirna_short = str_extract(
    #                   mirna, "(?<=hsa\\-)[:alnum:]+\\-[:alnum:]+")
    #                   ),
    #           by = c("mirna" = "mirna_short")) %>%
    # filter(!is.na(disease)) %>%
    # group_by(Cell_lineage, Cellular_process, mirna, group, label) %>%
    # tally() %>%
    # ungroup() %>%
    # filter(group == "Adaptive Immune Cells",
    #        str_detect(Cell_lineage, "^T"),
    #        str_detect(label, "^T")) %>%
    # distinct(Cell_lineage, label) %>%
    # mutate(Cell_lineage = str_replace(Cell_lineage, "T ", "T cells "),
    #        Cell_lineage = str_replace(Cell_lineage, " cells$", ""),
    #        label = str_replace_all(label, "\\.", " "),
    #        label = str_replace(label, "CD4", "helper"),
    #        label = str_replace(label, "CD8", "cytotoxic")) %>%
    # filter(!(label %in% Cell_lineage) & !(Cell_lineage %in% label)) %>%
    # mutate(lineage_match = str_detect(label, Cell_lineage)) %>%
    # group_by(Cell_lineage) %>%
    # summarize(num_matches = sum(lineage_match)) %>%
    I

Predicted binding targets

mirna_target_df <- read_tsv("../data/miRDB_v5.0_prediction_result.txt", col_names = FALSE) %>% 
    set_names(c("mirna", "gene", "score")) %>% 
    filter(str_detect(mirna, "hsa"))
mirna_mrna_target_df <- read_tsv("../data/synergizer.tsv", skip = 4) %>% 
    mutate(refseq_mrna = str_split(refseq_mrna, " ")) %>% 
    unnest(refseq_mrna) %>% 
    filter(refseq_mrna %in% mirna_target_df$gene) %>% 
    left_join(mirna_target_df, by = c("refseq_mrna" = "gene")) %>% 
    mutate(mirna_target = str_c(mirna, hgnc_symbol, sep = "_"))

 

Distribution of significant correlations across tumor types

Aggregate counts by correlate type

bind_rows(mirna_leuk_frac_corr_df, mirna_ciber_frac_corr_df, 
          mirna_mut_load_corr_df, mirna_tcr_div_corr_df,
          mirna_mrna_corr_df) %>% 
    group_by(disease, correlate_type) %>% 
    summarise(num_correlations = length(estimate),
              significant = sum(p.adjust < 0.05, na.rm = TRUE),
              other = num_correlations - significant) %>% 
    gather(correlations, total, -disease, -correlate_type, -num_correlations) %>% 
    ggplot(aes(x = disease, y = total)) +
    geom_col(aes(fill = disease, alpha = correlations), colour = "slategray") +
    theme(axis.text.x = element_text(angle = 45, hjust = 1),
          axis.text.y = element_text(angle = 45),
          legend.position = "top") +
    scale_fill_manual(values = tcga_colors$Color) +
    scale_alpha_manual(values = c(0.4, 1)) +
    guides(fill = FALSE) +
    facet_wrap(~ correlate_type, ncol = 2, scales = "free_y")

Defining more specific groups within correlate types

correlate_annotations <- mirna_mrna_corr_df %>% 
    select(disease, mirna, correlate, estimate, statistic, p.value, method, alternative, p.adjust, correlate_type) %>% 
    bind_rows(mirna_leuk_frac_corr_df, mirna_ciber_frac_corr_df, 
          mirna_mut_load_corr_df, .) %>% 
    distinct(correlate, correlate_type) %>% 
    left_join(gene_correlate_df,
              by = c("correlate" = "matched_name")) %>%  
    mutate(label = ifelse(correlate_type == "mRNA expression", 
                          `HGNC Symbol`, correlate),
           label = str_replace(correlate, "\\.\\.Tregs\\.", ""),
           label = ifelse(label == "leuk_frac", "Leukocyte Fraction", label),
           group = ifelse(correlate_type == "leukocyte fraction", "Total Immune Cells", group),
           group = ifelse(correlate_type == "mutation load", "Mutation Load", group),
           group = ifelse(correlate_type == "CIBERSORT fraction" & str_detect(label, "^(T|B)\\."), 
                          "Adaptive Immune Cells", group),
           group = ifelse(correlate_type == "CIBERSORT fraction" & !str_detect(label, "^(T|B)"), 
                          "Innate Immune Cells", group)) %>% 
    select(correlate, correlate_type, group, label)
mirna_sig_corr_df <- bind_rows(mirna_leuk_frac_corr_df, mirna_ciber_frac_corr_df, 
          mirna_mut_load_corr_df, mirna_tcr_div_corr_df,
          mirna_mrna_corr_df) %>% 
    filter(!(disease %in% c("DLBC", "THYM", "LAML"))) %>% 
    filter(!is.na(p.adjust), 
           p.adjust < 0.05) %>% 
    mutate(disease = factor(disease, levels = tcga_colors$Disease)) %>% 
    left_join(correlate_annotations, by = c("correlate", "correlate_type")) %>%
    filter(!is.na(group))
mirna_sig_corr_df %>% 
    mutate(direction = ifelse(estimate > 0, "positive", "negative")) %>%
    group_by(disease, correlate_type, direction) %>% 
    tally() %>% 
    ungroup() %>% 
    mutate(count = ifelse(direction == "negative", -1 * n, n)) %>% 
    ggplot(aes(x = disease, y = count)) +
    geom_col(aes(fill = disease, alpha = direction), colour = "slategray") +
    theme(axis.text.x = element_text(angle = 45, hjust = 1),
          legend.position = "top") +
    scale_fill_manual(values = tcga_colors$Color) +
    scale_alpha_manual(values = c(0.4, 1)) +
    guides(fill = FALSE) +
    facet_wrap(~ correlate_type, ncol = 2, scales = "free_y")

mirna_support_df <- mirna_sig_corr_df %>%
    select(disease, mirna, correlate, estimate, p.adjust, correlate_type,
           group, label) %>%
    mutate(mirna_target = str_c(mirna, correlate, sep = "_"),
           mirna_disease = str_c(mirna, disease, sep = "_"),
           mirna_short = str_extract(mirna, 
                                     "(?<=hsa\\-).*"),
           mirna_short_ambiguous = str_extract(mirna_short,
                                               "[:alnum:]+\\-[:alnum:]+")) %>% 
    mutate(significant = TRUE,
           immunomodulator = correlate_type == "mRNA expression",
           infiltrate = correlate_type %in% c("CIBERSORT fraction", "leukocyte fraction"),
           leukocyte = correlate_type == "leukocyte fraction",
           celltype = correlate_type == "CIBERSORT fraction",
           mirbase_support = (estimate < 0) & (correlate_type == "mRNA expression") &
               mirna_target %in% mirna_mrna_target_df$mirna_target,
           sygnal_support = (correlate_type %in% c("CIBERSORT fraction", "leukocyte fraction")) &
               mirna_disease %in% mirna_causal_df$mirna_disease,
           immune_support = mirna_short %in% mirna_immune_tidy_df$mirna |
               mirna_short_ambiguous %in% mirna_immune_tidy_df$mirna,
           strong = abs(estimate) > 0.5,
           immune_strong = immune_support & strong)
mirna_support_df %>% 
    gather(flag, status, significant, strong, immune_strong,
           immunomodulator, infiltrate, leukocyte, celltype,
           mirbase_support, sygnal_support, immune_support) %>% 
    group_by(mirna, flag) %>%
    summarize(support = n_distinct(disease[status])) %>%
    ungroup() %>% 
    filter(support > 0) %>% 
    group_by(flag) %>% 
    tally()
mirna_support_df %>% 
    gather(flag, status, significant, strong,
           immunomodulator, infiltrate, leukocyte, celltype,
           mirbase_support, sygnal_support, immune_support) %>% 
    group_by(disease, flag) %>%
    summarize(support = sum(status)) %>%
    ungroup() %>% 
    spread(flag, support) %>% 
    select(one_of(c("disease", "significant", "strong", "immune_support", 
             "immunomodulator", "mirbase_support",
             "infiltrate", "leukocyte", "celltype", "sygnal_support")))

Groups and correlates with greatest numbers of correlated miRNAs

mirna_sig_corr_df %>% 
    filter(!is.na(group)) %>% 
    group_by(correlate_type, group, label, disease) %>% 
    summarise(n_mirna = n_distinct(mirna)) %>% 
    ungroup() %>% 
    group_by(correlate_type, group, label) %>% 
    mutate(mean_mirna = mean(n_mirna)) %>% 
    ungroup() %>% 
    arrange(correlate_type, group, desc(mean_mirna)) %>% 
    mutate(group = fct_inorder(group),
           label = fct_inorder(label)) %>%
    ggplot(aes(x = label, y = n_mirna)) +
    geom_boxplot(aes(fill = group), 
                 outlier.size = 0, outlier.alpha = 0) +
    ggplot2::scale_fill_discrete() +
    guides(fill = guide_legend(title = NULL)) +
    ylab("Num. correlated miRNAs") +
    xlab("") +
    theme(axis.text.x = element_text(angle = 90, hjust = 1),
          legend.position = "top")

Frequently associated miRNAs

With leukocyte fraction(s) & causal prediction

mirna_leuk_common_df <- mirna_support_df %>%
    filter(sygnal_support & immune_support) %>% 
    group_by(mirna, disease) %>% 
    summarize(num_types = n_distinct(group)) %>% 
    ungroup() %>% 
    group_by(mirna) %>%
    mutate(nz_disease = n_distinct(disease[num_types > 0])) %>%
    ungroup() %>%
    filter(nz_disease > 2)
mirna_leuk_common_df %>% 
    ggplot(aes(x = disease, y = mirna)) +
    geom_tile(aes(fill = num_types)) +
    theme(axis.text.x = element_text(angle = 90, hjust = 1))

plot_colors <- tcga_colors %>% 
    filter(Disease %in% mirna_leuk_common_df$disease)
p <- mirna_support_df %>% 
    filter(sygnal_support & immune_support,
           mirna %in% mirna_leuk_common_df$mirna) %>% 
    mutate(group = fct_inorder(group),
           mirna = str_extract(mirna, "miR.*"),
           correlation = ifelse(estimate > 0, "positive", "negative"),
           disease = factor(disease, levels = plot_colors$Disease)) %>%
    ggplot(aes(y = label, x = disease)) +
    geom_tile(aes(fill = disease, colour = correlation), size = 0.4) +
    scale_fill_manual("Tumor Group", values = plot_colors$Color) +
    scale_colour_manual("Correlation", values = c(my_cet_pal[1], my_cet_pal[256])) +
    guides(fill = guide_legend(nrow = 3, byrow = TRUE),
           colour = guide_legend(override.aes = list(fill = "#CCCCCC"),
                                 reverse = TRUE)) +
    ylab("") +
    xlab("") +
    theme(axis.text.x = element_blank(),
          axis.ticks.x = element_blank(),
          legend.position = "top",
          strip.text.x = element_text(face = "bold", angle = 90, hjust = 0),
          strip.text.y = element_text(angle = 0, face = "bold", hjust = 0),
          strip.background = element_blank()) +
    facet_grid(group ~ mirna, scales = "free", space = "free")
p

ggsave("../figures/mirna_leuk_supported_corr.png", p,
       width = 17, height = 8, units = "cm", dpi = 300, scale = 2)

With immunomodulators & predicted targets

mirna_immunomod_common_df %>% 
    ggplot(aes(x = disease, y = mirna)) +
    geom_tile(aes(fill = num_types)) +
    theme(axis.text.x = element_text(angle = 90, hjust = 1))

plot_colors <- tcga_colors %>% 
    filter(Disease %in% mirna_immunomod_common_df$disease)
p <- mirna_support_df %>% 
    filter(mirbase_support & immune_support,
           mirna %in% mirna_immunomod_common_df$mirna) %>% 
    mutate(group = fct_inorder(group),
           mirna = str_extract(mirna, "miR.*"),
           correlation = ifelse(estimate > 0, "positive", "negative"),
           disease = factor(disease, levels = plot_colors$Disease)) %>%
    ggplot(aes(y = label, x = disease)) +
    geom_tile(aes(fill = disease), 
              colour = my_cet_pal[1], size = 0.4) +
    scale_fill_manual("Tumor Group", values = plot_colors$Color) +
    guides(fill = guide_legend(nrow = 3, byrow = TRUE)) +
    ylab("") +
    xlab("") +
    theme(axis.text.x = element_blank(),
          axis.ticks.x = element_blank(),
          legend.position = "top",
          strip.text.x = element_text(face = "bold", angle = 90, hjust = 0),
          strip.text.y = element_text(angle = 0, face = "bold", hjust = 0),
          strip.background = element_blank()) +
    facet_grid(group ~ mirna, scales = "free", space = "free")
p

ggsave("../figures/mirna_immunomod_supported_corr.png", p,
       width = 17, height = 5, units = "cm", dpi = 300, scale = 2)

All categories…

mirna_all_common_df <- mirna_support_df %>%
    group_by(mirna, disease) %>% 
    summarize(all_support = sum(mirbase_support) > 0 &
                  sum(sygnal_support) > 0 &
                  sum(immune_support) > 0) %>% 
    filter(all_support) %>% 
    group_by(mirna) %>%
    mutate(nz_disease = n_distinct(disease)) %>%
    filter(nz_disease > 1)
p <- mirna_support_df %>% 
    filter(mirna %in% mirna_all_common_df$mirna,
           correlate_type != "mutation load") %>% 
    mutate(support = (mirbase_support | sygnal_support) & immune_support,
           group = fct_inorder(group),
           mirna_short = str_extract(mirna, "miR.*")) %>%
    group_by(mirna, label) %>% 
    mutate(num_diseases = n_distinct(disease)) %>% 
    group_by(group) %>% 
    mutate(label = fct_reorder(label, num_diseases)) %>% 
    ungroup() %>% 
    ggplot(aes(y = label, x = disease)) +
    geom_tile(aes(fill = estimate, colour = support, size = support)) +
    scale_fill_gradientn("Correlation", colours = my_cet_pal) +
    scale_colour_manual("Support", values = c("#333333", "#E69F00")) +
    scale_size_manual("Support", values = c(0.1, 1)) +
    ylab("") +
    xlab("") +
    theme(axis.text.x = element_text(angle = 45, hjust = 1),
          legend.position = "top",
          strip.text.x = element_text(face = "bold"),
          strip.text.y = element_text(angle = 0, face = "bold", hjust = 0),
          strip.background = element_blank()) +
    facet_grid(group ~ mirna_short, scales = "free", space = "free")
p

ggsave("../figures/mir142_supported_corr.png", p,
       width = 16, height = 16, units = "cm", dpi = 300, scale = 2)
LS0tCnRpdGxlOiAiUGFuQ2FuIElSV0cgbWlSTkEgQ29ycmVsYXRpdmUgQW5hbHlzaXMiCmF1dGhvcjogIkphbWVzIEEuIEVkZHkiCmRhdGU6ICdgciBsdWJyaWRhdGU6OnRvZGF5KClgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgZmlnX3dpZHRoOiA4CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKLS0tCgojIFN1bW1hcnkKCkV4cGxvcmluZyBhc3NvY2lhdGlvbnMgb2YgbWlSTkEgZXhwcmVzc2lvbiBsZXZlbHMgYWNyb3NzIHR1bW9yIHR5cGVzIGluIFRDR0Egd2l0aCBzdWdnZXN0ZWQgY29ycmVsYXRlcyBkZWZpbmVkIGJ5IHRoZSBJbW11bmUgUmVzcG9uc2UgV29ya2luZyBHcm91cDoKCisgT3ZlcmFsbCBMZXVrb2N5dGUgZnJhY3Rpb24gIAorIEluZGl2aWR1YWwgUmVsYXRpdmUgQ0lCRVJTT1JUIGZyYWN0aW9uICAKKyBNdXRhdGlvbiBMb2FkICAKKyBUQ1IsQkNSIERpdmVyc2l0eSAgCisgRXhwcmVzc2lvbiBvZiAgCiAgICArIElGTkcsSUZOLWdhbW1hICAKICAgICsgUFJGMSxQZXJmb3JpbiAgCiAgICArIEdaTUEsR3Jhbnp5bWUgQSAgCiAgICArIFBEQ0QxLFBELTEgIAogICAgKyBDRDI3NCxQRC1MMSAgCiAgICArIFBEQ0QxTEcyLFBELUwyICAKICAgICsgSUwxMCxJTC0xMCAgCiAgICArIFRHRkIxLFRHRi1iZXRhICAKICAgICsgSURPMSxJRE8gIAogICAgKyBITEEtQSAgCgoKYGBge3Igc2V0dXAsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0UsIGNhY2hlPUZBTFNFfQojIFN5bmFwc2UgY2xpZW50CmxpYnJhcnkoc3luYXBzZUNsaWVudCkKCiMgcGFyYWxsZWwgY29tcHV0aW5nCmxpYnJhcnkocGFyYWxsZWwpCgojIHZpeiBwYWNrYWdlcwpsaWJyYXJ5KGdndGhlbWVzKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2diZWVzd2FybSkKCiMgcGFja2FnZXMgZm9yIGdlbmVyYWwgZGF0YSBtdW5naW5nLCBmb3JtYXR0aW5nCmxpYnJhcnkoZmVhdGhlcikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGZvcmNhdHMpCmxpYnJhcnkoYnJvb20pCmxpYnJhcnkodGlkeXZlcnNlKQoKbXlfdGhlbWVfYncgPC0gZnVuY3Rpb24oKSB7CiAgICB0aGVtZV9idygpICsKICAgICAgICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSByZWwoMC45KSksCiAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIikpCn0KZ2dwbG90Mjo6dGhlbWVfc2V0KG15X3RoZW1lX2J3KCkpCnNjYWxlX2ZpbGxfZGlzY3JldGUgPC0gZnVuY3Rpb24oLi4uKSBzY2FsZV9maWxsX2NvbG9yYmxpbmQoLi4uKQpzY2FsZV9maWxsX2NvbnRpbnVvdXMgPC0gZnVuY3Rpb24oLi4uKSBzY2FsZV9maWxsX3ZpcmlkaXMoLi4uKQpzY2FsZV9jb2xvdXJfZGlzY3JldGUgPC0gZnVuY3Rpb24oLi4uKSBzY2FsZV9jb2xvcl9jb2xvcmJsaW5kKC4uLikKc2NhbGVfY29sb3VyX2NvbnRpbnVvdXMgPC0gZnVuY3Rpb24oLi4uKSBzY2FsZV9jb2xvcl92aXJpZGlzKC4uLikKYGBgCgotLS0tLQoKXCAgCgojIFByZXBhcmUgZGF0YQoKIyMgUmV0cmlldmUgbWlSTkEgZGF0YSBmcm9tIFN5bmFwc2UKCkRhdGEgYXJlIHN0b3JlZCBvbiBTeW5hcHNlIGluIHRoZSBmb2xkZXIgYHN5bjYxNzExMDlgLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmlmICghZGlyLmV4aXN0cygiZGF0YSIpKSB7CiAgICBkaXIuY3JlYXRlKCJkYXRhIikKfQpzeW5hcHNlTG9naW4oKQptaXJuYV9zeW5mb2xkZXIgPC0gInN5bjYxNzExMDkiCm1pcm5hX3N5bmZpbGVzIDwtIHN5bmFwc2VRdWVyeSgKICAgIHNwcmludGYoJ3NlbGVjdCAqIGZyb20gZmlsZSB3aGVyZSBwYXJlbnRJZD09IiVzIicsIG1pcm5hX3N5bmZvbGRlcikKKQoKIyBkb3dubG9hZCBmaWxlcyBhbmQgc3RvcmUgZGF0YS9wYXRocyBpbiBuZXcgZGF0YSBmcmFtZQptaXJuYV9maWxlcyA8LSBtaXJuYV9zeW5maWxlcyAlPiUgCiAgICBtdXRhdGUoZmlsZV9kYXRhID0gbWFwKGZpbGUuaWQsIGZ1bmN0aW9uKHN5bmlkKSB7CiAgICAgICAgc3luR2V0KHN5bmlkLCBkb3dubG9hZExvY2F0aW9uID0gIi4uL2RhdGEvdGNnYS8iKQogICAgfSksCiAgICBmaWxlX3BhdGggPSBtYXBfY2hyKGZpbGVfZGF0YSwgZ2V0RmlsZUxvY2F0aW9uKSkKYGBgCgojIyBMb2FkIG1pUk5BIHNhbXBsZSBkYXRhCgpTYW1wbGUgY2hhcmFjdGVyaXN0aWNzIGFyZSBzdG9yZWQgaW4gYSB0YWItZGVsaW1pdGVkIHRleHQgZmlsZSAoU3luYXBzZSBJRDogYHN5bjcyMjIwMTBgKSBhbmQgY2FuIGJlIGxvYWRlZCB3aXRoIGByZWFkX3RzdigpYC4KCmBgYHtyfQojIGxvYWQgc2FtcGxlIGRhdGEKbWlybmFfc2FtcGxlX2ZpbGUgPC0gbWlybmFfZmlsZXMgJT4lIAogICAgZmlsdGVyKGZpbGUuaWQgPT0gInN5bjcyMjIwMTAiKSAlPiUgCiAgICAuW1siZmlsZV9wYXRoIl1dCm1pcm5hX3NhbXBsZV9kZiA8LSByZWFkX3RzdihtaXJuYV9zYW1wbGVfZmlsZSkKYGBgCgojIyBTYW1wbGUgZmlsdGVyaW5nCgpTYW1wbGUgUXVhbGl0eSBBbm5vdGF0aW9uczoKc3luNDU1MTI0OCAoIm1lcmdlZF9zYW1wbGVfcXVhbGl0eV9hbm5vdGF0aW9ucy50c3YiKQoKYGBge3J9CnNhbXBsZV9xdWFsX2ZpbGUgPC0gc3luR2V0KCJzeW40NTUxMjQ4IiwgZG93bmxvYWRMb2NhdGlvbiA9ICIuLi9kYXRhL3RjZ2EvIikKc2FtcGxlX3F1YWxfZGYgPC0gc2FtcGxlX3F1YWxfZmlsZSAlPiUgCiAgICBnZXRGaWxlTG9jYXRpb24oKSAlPiUgCiAgICByZWFkX3RzdigpCmBgYAoKcmVtb3ZlIHNhbXBsZXMgYmFzZWQgb24gYERvX25vdF91c2U9VHJ1ZWAsIGFuZCByZW1vdmUgY2FzZXMgd2l0aCBgQVdHX2V4Y2x1ZGVkX2JlY2F1c2Vfb2ZfcGF0aG9sb2d5PVRydWVgCgpgYGB7cn0KIyBzYW1wbGVzIHRvIGV4Y2x1ZGUgZnJvbSBhbGwgZGF0YXNldHMKZXhjbHVkZV9zYW1wbGVzIDwtIHNhbXBsZV9xdWFsX2RmICU+JSAKICAgIG11dGF0ZShBV0dfZXhjbHVkZWRfYmVjYXVzZV9vZl9wYXRob2xvZ3kgPSBwYXJzZV9sb2dpY2FsKEFXR19leGNsdWRlZF9iZWNhdXNlX29mX3BhdGhvbG9neSksCiAgICAgICAgICAgRG9fbm90X3VzZSA9IHBhcnNlX2xvZ2ljYWwoc3RyX3RvX3VwcGVyKERvX25vdF91c2UpKSkgJT4lIAogICAgZmlsdGVyKEFXR19leGNsdWRlZF9iZWNhdXNlX29mX3BhdGhvbG9neSB8IERvX25vdF91c2UpCmBgYAoKUmVtb3ZlIHNhbXBsZXMgZnJvbSBtaVJOQSBkYXRhc2V0LgoKYGBge3J9Cm1pcm5hX3NhbXBsZV9kZiA8LSBtaXJuYV9zYW1wbGVfZGYgJT4lIAogICAgZmlsdGVyKCEoaWQgJWluJSBleGNsdWRlX3NhbXBsZXMkYWxpcXVvdF9iYXJjb2RlKSkKYGBgCgpcICAKCiMjIExvYWQgbWlSTkEgZXhwcmVzc2lvbiBkYXRhCgptaVJOQSBub3JtYWxpemVkLCBiYXRjaCBjb3JyZWN0ZWQgZXhwcmVzc2lvbiB2YWx1ZXMgZm9yIGFsbCBzYW1wbGVzIGFyZSBzdG9yZWQgYXMgYSBtYXRyaXggaW4gYSBDU1YgZmlsZSAoU3luYXBzZSBJRDogYHN5bjcyMDEwNTNgKSBhbmQgY2FuIGJlIGxvYWRlZCB3aXRoIGByZWFkX2NzdigpYC4KCmBgYHtyIHdhcm5pbmc9RkFMU0V9Cm1pcm5hX2NvcnJfZmlsZSA8LSAiLi4vZGF0YS9pbnRlcm1lZGlhdGUvbWlybmFfY29ycmVsYXRlX2RhdGEuZmVhdGhlciIKZm9yY2VfcmVhZCA8LSBGQUxTRQppZiAoIWZpbGUuZXhpc3RzKG1pcm5hX2NvcnJfZmlsZSkgfCBmb3JjZV9yZWFkKSB7CiAgICAjIGxvYWQgbm9ybWFsaXplZCwgYmF0Y2gtY29ycmVjdGVkIGV4cHJlc3Npb24gZGF0YQogICAgbWlybmFfbm9ybV9maWxlIDwtIG1pcm5hX2ZpbGVzICU+JSAKICAgICAgICBmaWx0ZXIoZmlsZS5pZCA9PSAic3luNzIwMTA1MyIpICU+JSAKICAgICAgICAuW1siZmlsZV9wYXRoIl1dCiAgICBtaXJuYV9ub3JtX2RmIDwtIHJlYWRfY3N2KG1pcm5hX25vcm1fZmlsZSwgcHJvZ3Jlc3MgPSBGQUxTRSkKICAgIAogICAgbWlybmFfY29ycl9kZiA8LSBtaXJuYV9ub3JtX2RmICU+JSAKICAgICAgICBzZWxlY3Qob25lX29mKGMoIkdlbmVzIiwgbWlybmFfc2FtcGxlX3B0X2RmJGlkKSkpCiAgICAKICAgIHdyaXRlX2ZlYXRoZXIobWlybmFfY29ycl9kZiwgbWlybmFfY29ycl9maWxlKQogICAgcm0obWlybmFfbm9ybV9kZikKfSBlbHNlIHsKICAgIG1pcm5hX2NvcnJfZGYgPC0gcmVhZF9mZWF0aGVyKG1pcm5hX2NvcnJfZmlsZSkKfQpgYGAKClwgIAoKIyMgU2V0IHVwIHBsb3R0aW5nIGNvbG9ycwoKYGBge3J9CnRjZ2FfY29sb3JzIDwtIHRyaWJibGUoCiAgICB+Q29sb3IsIH5EaXNlYXNlLAogICAgIiNFRDI4OTEiLCAiQlJDQSIsCiAgICAiI0IyNTA5RSIsICJHQk0iLAogICAgIiNENDlEQzciLCAiTEdHIiwKICAgICIjQzFBNzJGIiwgIkFDQyIsCiAgICAiI0U4QzUxRCIsICJQQ1BHIiwKICAgICIjRjlFRDMyIiwgIlRIQ0EiLAogICAgIiMxMDRBN0YiLCAiQ0hPTCIsCiAgICAiIzlFRERGOSIsICJDT0FEIiwKICAgICIjMDA3RUI1IiwgIkVTQ0EiLAogICAgIiNDQUNDREIiLCAiTElIQyIsCiAgICAiIzZFN0JBMiIsICJQQUFEIiwKICAgICIjREFGMUZDIiwgIlJFQUQiLAogICAgIiMwMEFFRUYiLCAiU1RBRCIsCiAgICAiI0Y2QjY2NyIsICJDRVNDIiwKICAgICIjRDk3RDI1IiwgIk9WIiwKICAgICIjRkJFM0M3IiwgIlVDRUMiLAogICAgIiNGODk0MjAiLCAiVUNTIiwKICAgICIjOTdEMUE5IiwgIkhOU0MiLAogICAgIiMwMDk0NDQiLCAiVVZNIiwKICAgICIjNzU0QzI5IiwgIkxBTUwiLAogICAgIiNDRUFDOEYiLCAiVEhZTSIsCiAgICAiIzM5NTNBNCIsICJETEJDIiwKICAgICIjQkJENjQyIiwgIlNLQ00iLAogICAgIiMwMEE5OUQiLCAiU0FSQyIsCiAgICAiI0QzQzNFMCIsICJMVUFEIiwKICAgICIjQTA4NEJEIiwgIkxVU0MiLAogICAgIiM1NDJDODgiLCAiTUVTTyIsCiAgICAiI0ZBRDJEOSIsICJCTENBIiwKICAgICIjRUQxQzI0IiwgIktJQ0giLAogICAgIiNGOEFGQjMiLCAiS0lSQyIsCiAgICAiI0VBNzA3NSIsICJLSVJQIiwKICAgICIjN0UxOTE4IiwgIlBSQUQiLAogICAgIiNCRTFFMkQiLCAiVEdDVCIKKQoKbWlybmFfc2FtcGxlX2RmIDwtIG1pcm5hX3NhbXBsZV9kZiAlPiUgCiAgICBtdXRhdGUoRGlzZWFzZSA9IGZhY3RvcihEaXNlYXNlLCBsZXZlbHMgPSB0Y2dhX2NvbG9ycyREaXNlYXNlKSkKYGBgCgotLS0tLQoKXCAgCgojIEV4cGxvcmUgbWlSTkEgZGF0YQoKIyMgU2FtcGxlIGNoYXJhY3RlcmlzdGljcwoKVGhlIHRhYmxlIGBtaXJuYV9zYW1wbGVfZGZgIGNvbnRhaW5zIGByIG5jb2wobWlybmFfc2FtcGxlX2RmKWAgY29sdW1ucyBkZXNjcmliaW5nIHRoZSBgciBucm93KG1pcm5hX3NhbXBsZV9kZilgIHNhbXBsZXMgaW4gdGhlIGRhdGEuIFRoZSBgU2FtcGxlX1R5cGVgIGNvbHVtbiBjb3JyZXNwb25kcyB0byBUQ0dBICoqc2FtcGxlIHR5cGUgY29kZXMqKiwgd2hpY2ggYXJlIGRlZmluZWQgW2hlcmVdKGh0dHBzOi8vZ2RjLmNhbmNlci5nb3YvcmVzb3VyY2VzLXRjZ2EtdXNlcnMvdGNnYS1jb2RlLXRhYmxlcy9zYW1wbGUtdHlwZS1jb2RlcykuIFRoZSBmb2xsb3dpbmcgc2FtcGxlIHR5cGVzIGFyZSBpbmNsdWRlZCBpbiB0aGUgbWlSTkEgZGF0YToKCmBgYHtyfQptaXJuYV9zYW1wbGVfZGYgJT4lIAogICAgZ3JvdXBfYnkoU2FtcGxlX1R5cGUpICU+JSAKICAgIHRhbGx5KCkKYGBgCgpcICAKCkJhc2VkIG9uIHRoZSBjb2RlcywgdGhpcyBpcyB0aGUgZGlzdHJpYnV0aW9uIG9mIHNhbXBsZSB0eXBlczoKCmBgYHtyfQptaXJuYV9zYW1wbGVfZGYgJT4lIAogICAgZ3JvdXBfYnkoU2FtcGxlX1R5cGUpICU+JSAKICAgIHRhbGx5KCkgJT4lIAogICAgbXV0YXRlKERlZmluaXRpb24gPSBjYXNlX3doZW4oCiAgICAgICAgLiRTYW1wbGVfVHlwZSA9PSAxIH4gIlByaW1hcnkgU29saWQgVHVtb3IiLAogICAgICAgIC4kU2FtcGxlX1R5cGUgPT0gMiB+ICJSZWN1cnJlbnQgU29saWQgVHVtb3JyIiwKICAgICAgICAuJFNhbXBsZV9UeXBlID09IDMgfiAiUHJpbWFyeSBCbG9vZCBEZXJpdmVkIENhbmNlciAtIFBlcmlwaGVyYWwgQmxvb2QiLAogICAgICAgIC4kU2FtcGxlX1R5cGUgPT0gNSB+ICJBZGRpdGlvbmFsIC0gTmV3IFByaW1hcnkiLAogICAgICAgIC4kU2FtcGxlX1R5cGUgPT0gNiB+ICJNZXRhc3RhdGljIiwKICAgICAgICAuJFNhbXBsZV9UeXBlID09IDcgfiAiQWRkaXRpb25hbCBNZXRhc3RhdGljIiwKICAgICAgICAuJFNhbXBsZV9UeXBlID09IDExIH4gIlNvbGlkIFRpc3N1ZSBOb3JtYWwiCiAgICApKQpgYGAKCk5vdGU6IG9ubHkgaW5jbHVkZSAicHJpbWFyeSIgdHVtb3Igc2FtcGxlcyBmb3Igbm93OyBhbHNvLCBhZGQgYWRkaXRpb25hbCBjb2x1bW4gdG8gc3RvcmUgdmlhbCBJRCAodG8gZWFzZSBtYXBwaW5nIGJldHdlZW4gZGF0YSBzZXRzKS4KCmBgYHtyfQptaXJuYV9zYW1wbGVfcHRfZGYgPC0gbWlybmFfc2FtcGxlX2RmICU+JSAKICAgIGZpbHRlcihTYW1wbGVfVHlwZSAlaW4lIGMoMSwgMywgNSkpICU+JSAKICAgIG11dGF0ZSh2aWFsX2lkID0gc3RyX3JlcGxhY2UoaWQsICIoXFwtWzphbG51bTpdKyl7M30kIiwgIiIpKQpgYGAKClwgIAoKQWRkaXRpb25hbGx5LCB0aGUgZm9sbG93aW5nIGRpc2Vhc2UgdHlwZXMgYXJlIGluY2x1ZGVkOgoKYGBge3J9Cm1pcm5hX3NhbXBsZV9wdF9kZiAlPiUgCiAgICBncm91cF9ieShEaXNlYXNlKSAlPiUgCiAgICB0YWxseSgpCmBgYAoKTm90ZTogZXhjbHVkZSBMQU1MLCBUSFlNLCBhbmQgRExCQyBmcm9tIGNlbGwgY29udGVudCBjb3JyZWxhdGlvbnMKClwgIAoKQW5kIHRlY2huaWNhbCB2YXJpYWJsZXM6CgpgYGB7cn0KbWlybmFfc2FtcGxlX3B0X2RmICU+JSAKICAgIGdyb3VwX2J5KFByb3RvY29sLCBQbGF0Zm9ybSkgJT4lIAogICAgdGFsbHkoKQpgYGAKClwgIAoKIyMgbWlSTkEgZXhwcmVzc2lvbiB2YWx1ZXMKCkV4cHJlc3Npb24gdmFsdWVzIGluIHRoZSBkYXRhIGFyZSByZXBvcnRlZGx5IHJlYWRzIHBlciBtaWxsaW9uIChSUE0pLiBJJ3ZlIHJhbmRvbWx5IHNlbGVjdGVkIGEgZmV3IHNhbXBsZXMgZnJvbSBlYWNoIGRpc2Vhc2UgdHlwZSwgc2FtcGxlIHR5cGUsIHByb3RvY29sLCBhbmQgcGxhdGZvcm0gdG8gaW5zcGVjdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGV4cHJlc3Npb24gdmFsdWVzIGFjcm9zcyBhbGwgYHIgbnJvdyhtaXJuYV9jb3JyX2RmKWAgbWlSTkEgZ2VuZXMuCgpgYGB7cn0Kc2V0LnNlZWQoMCkKCiMgcmFuZG9tbHkgc2VsZWN0IDEtMyBzYW1wbGVzIGZyb20gZWFjaCBjb21iaW5hdGlvbiBvZiBjaGFyYWN0ZXJpc3RpY3MKc2FtcGxlX3N1Yl9kZiA8LSBtaXJuYV9zYW1wbGVfcHRfZGYgJT4lIAogICAgZ3JvdXBfYnkoRGlzZWFzZSwgU2FtcGxlX1R5cGUsIFByb3RvY29sLCBQbGF0Zm9ybSkgJT4lIAogICAgc2FtcGxlX24oMywgcmVwbGFjZSA9IFRSVUUpICU+JSAKICAgIHVuZ3JvdXAoKSAlPiUgCiAgICBkaXN0aW5jdCgpCgojIHN1YnNldCBhbmQgbWVsdCB0aGUgZXhwcmVzc2lvbiBkYXRhCm1pcm5hX3N1Yl9kZiA8LSBtaXJuYV9jb3JyX2RmICU+JSAKICAgIHNlbGVjdChvbmVfb2YoYygiR2VuZXMiLCBzYW1wbGVfc3ViX2RmJGlkKSkpICU+JQogICAgZ2F0aGVyKCJzYW1wbGUiLCAiZXhwcmVzc2lvbiIsIC1HZW5lcykKYGBgCgpcICAKCkV2ZW4gd2l0aCBhIHNoaWZ0ZWQgbG9nIChgbG9nMTAoeCArIDEpYCkgdHJhbnNmb3JtYXRpb24sIGV4cHJlc3Npb24gdmFsdWVzIGxvb2sgdG8gYmUgbW9yZSBleHBvbmVudGlhbGx5IGRpc3RyaWJ1dGVkIHRoYW4gbm9ybWFsLgoKYGBge3J9Cm1pcm5hX3N1Yl9kZiAlPiUgCiAgICBsZWZ0X2pvaW4obWlybmFfc2FtcGxlX3B0X2RmLCBieSA9IGMoInNhbXBsZSIgPSAiaWQiKSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gbG9nMTAoZXhwcmVzc2lvbiArIDEpKSkgKwogICAgc3RhdF9kZW5zaXR5KGFlcyhncm91cCA9IHNhbXBsZSksIGdlb20gPSAibGluZSIsIHBvc2l0aW9uID0gImlkZW50aXR5IiwgCiAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjIpCmBgYAoKXCAgCgpJIGNhbiBhbHNvIGxvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBsb2ctUlBNIHZhbHVlcyB3aXRoIGJveHBsb3RzOgoKYGBge3J9Cm1pcm5hX3N1Yl9kZiAlPiUgCiAgICBsZWZ0X2pvaW4obWlybmFfc2FtcGxlX3B0X2RmLCBieSA9IGMoInNhbXBsZSIgPSAiaWQiKSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gc2FtcGxlLCB5ID0gbG9nMTAoZXhwcmVzc2lvbiArIDEpKSkgKwogICAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsID0gRGlzZWFzZSksIG91dGxpZXIuc2l6ZSA9IDAuNSkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHRjZ2FfY29sb3JzJENvbG9yKQpgYGAKCgpgYGB7ciwgd2FybmluZz1GQUxTRX0KIyBjb252ZXJ0IGV4cHJlc3Npb24gZGYgdG8gbWF0cml4Cm1pcm5hX2NvcnJfbWF0IDwtIG1pcm5hX2NvcnJfZGYgJT4lIAogICAgc2VsZWN0KG9uZV9vZihjKCJHZW5lcyIsIG1pcm5hX3NhbXBsZV9wdF9kZiRpZCkpKSAlPiUgCiAgICBjb2x1bW5fdG9fcm93bmFtZXMoIkdlbmVzIikgJT4lIAogICAgYXMubWF0cml4KCkKYGBgClwgIAoKIyMgUENBCgpJIHVzZWQgdGhlIGBwcmNvbXAoKWAgZnVuY3Rpb24gb24gdGhlIHRyYW5zcG9zZWQgZXhwcmVzc2lvbiBtYXRyaXggKHNhbXBsZXMgeCBnZW5lcywgYWZ0ZXIgdHJhbnNwb3NpbmcpIHRvIGNvbXB1dGUgUENBIGRhdGEsIHdoaWNoIEkgY2FuIHVzZSB0byBsb29rIGZvciBiYXRjaCBlZmZlY3RzIGFtb25nIHNhbXBsZXMuCgpgYGB7cn0KbWlybmFfY29ycl9wY2EgPC0gbWlybmFfY29ycl9tYXQgJT4lIAogICAgdCgpICU+JSAKICAgIHByY29tcCgpCmBgYAoKKEkgY2FuIHVzZSB0aGUgYGJyb29tOnRpZHkoKWAgZnVuY3Rpb24gdG8gY29udmVydCBkYXRhIGluIHRoZSBgcHJjb21wYCBvYmplY3QgaW50byBkYXRhIGZyYW1lcyBmb3IgYGdncGxvdGAuKQoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CnBjX2RmIDwtIG1pcm5hX2NvcnJfcGNhICU+JSAKICAgIHRpZHkoInBjcyIpCgptaXJuYV9jb3JyX3BjYV9kZiA8LSBtaXJuYV9jb3JyX3BjYSAlPiUgCiAgICB0aWR5KCJzYW1wbGVzIikgJT4lIAogICAgZmlsdGVyKFBDIDw9IDIpICU+JSAKICAgIGxlZnRfam9pbihtaXJuYV9zYW1wbGVfcHRfZGYsIGJ5ID0gYygicm93IiA9ICJpZCIpKQoKcm0obWlybmFfY29ycl9tYXQpCmBgYAoKVGhlIHBsb3QgYmVsb3cgc2hvd3Mgc2FtcGxlcyBwbG90dGVkIGFzIHBvaW50cyBhbG9uZyB0aGUgZmlyc3QgdHdvIHByaW5jaXBsZSBjb21wb25lbnRzIChQQ3MpLiBQb2ludHMgYXJlIGNvbG9yZWQgYnkgZGlzZWFzZSB0eXBlLgoKYGBge3J9CnBjMV9sYWJlbCA8LSBwY19kZiAlPiUgCiAgICBmaWx0ZXIoUEMgPT0gMSkgJT4lIAogICAgdHJhbnNtdXRlKGxhYmVsID0gc3ByaW50ZigiUEMlcyBbJTAuMmYlJV0iLCBQQywgMTAwKnBlcmNlbnQpKSAlPiUgCiAgICBmbGF0dGVuX2NocigpCgpwYzJfbGFiZWwgPC0gcGNfZGYgJT4lIAogICAgZmlsdGVyKFBDID09IDIpICU+JSAKICAgIHRyYW5zbXV0ZShsYWJlbCA9IHNwcmludGYoIlBDJXMgWyUwLjJmJSVdIiwgUEMsIDEwMCpwZXJjZW50KSkgJT4lIAogICAgZmxhdHRlbl9jaHIoKQoKbWlybmFfY29ycl9wY2FfZGYgJT4lIAogICAgc3ByZWFkKFBDLCB2YWx1ZSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gYDFgLCB5ID0gYDJgKSkgKwogICAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gRGlzZWFzZSkpICsKICAgIHhsYWIocGMxX2xhYmVsKSArCiAgICB5bGFiKHBjMl9sYWJlbCkgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHRjZ2FfY29sb3JzJENvbG9yKQpgYGAKCi0tLS0tCgpcICAKCiMgUHJlcGFyZSBjb3JyZWxhdGUgZGF0YQoKKyBPdmVyYWxsIExldWtvY3l0ZSBmcmFjdGlvbiAgCisgSW5kaXZpZHVhbCBSZWxhdGl2ZSBDSUJFUlNPUlQgZnJhY3Rpb24gIAorIE11dGF0aW9uIExvYWQgIAorIFRDUixCQ1IgRGl2ZXJzaXR5ICAKKyBHZW5lIEV4cHJlc3Npb24KCiMjIExldWtvY3l0ZSBmcmFjdGlvbgoKIyMjIFJldHJpZXZlL2xvYWQgZGF0YQoKQ2VsbHVsYXIgQ29udGVudDogc3luNzk5NDcyOApzeW41ODA4MjA1ICgiVENHQV9hbGxfbGV1a19lc3RpbWF0ZS5tYXNrZWQuMjAxNzAxMDcudHN2IikKCmBgYHtyfQojIGZpbGVfZGF0YSA8LSBzeW5HZXQoInN5bjU4MDgyMDUiLCBkb3dubG9hZExvY2F0aW9uID0gIi4vIikKbGV1a19mcmFjX2ZpbGUgPC0gIi4uL2RhdGEvdGNnYS9UQ0dBX2FsbF9sZXVrX2VzdGltYXRlLm1hc2tlZC4yMDE3MDEwNy50c3YiCmxldWtfZnJhY19kZiA8LSByZWFkX3RzdihsZXVrX2ZyYWNfZmlsZSwgY29sX25hbWVzID0gRkFMU0UpICU+JSAKICAgIHNldF9uYW1lcyhjKCJkaXNlYXNlIiwgImlkIiwgImxldWtfZnJhYyIpKQpgYGAKCiMjIyBTYW1wbGUgZmlsdGVyaW5nCgpgYGB7cn0KbGV1a19mcmFjX2RmIDwtIGxldWtfZnJhY19kZiAlPiUKICAgIGZpbHRlcighKGlkICVpbiUgZXhjbHVkZV9zYW1wbGVzJGFsaXF1b3RfYmFyY29kZSkpCmBgYAoKCiMjIyBTYW1wbGUgbWF0Y2hpbmcKCklkZW50aWZ5IG1hdGNoZWQgc2FtcGxlcyBiZXR3ZWVuIG1pUk5BIGFuZCBsZXVrb2N5dGUgZnJhY3Rpb24uCgpgYGB7cn0KbGV1a19mcmFjX2lkcyA8LSBsZXVrX2ZyYWNfZGYgJT4lIAogICAgc2VsZWN0KGlkKSAlPiUKICAgIG11dGF0ZSh2aWFsX2lkID0gc3RyX3JlcGxhY2UoaWQsICIoXFwtWzphbG51bTpdKyl7M30kIiwgIiIpKSAlPiUgCiAgICBhcnJhbmdlKCkKCm1pcm5hX2lkcyA8LSBtaXJuYV9zYW1wbGVfcHRfZGYgJT4lCiAgICBzZWxlY3QoaWQsIHZpYWxfaWQpICU+JQogICAgYXJyYW5nZSgpCgptaXJuYV9sZXVrX2ZyYWNfc2hhcmVkX2lkcyA8LSBpbm5lcl9qb2luKG1pcm5hX2lkcywgbGV1a19mcmFjX2lkcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAidmlhbF9pZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4ID0gYygiX21pcm5hIiwgIl9sZXVrX2ZyYWMiKSkKCiMgb25seSBrZWVwIHNhbXBsZXMgd2l0aCBtYXRjaGVkIHZpYWwgSUQgQU5EIHBvcnRpb24gbnVtYmVyCnBvcnRpb25faWRfbWludXNfYW5hbHl0ZV9yZWdleCA8LSAiKFs6YWxudW06XStcXC0pezR9WzAtOV0rIgojIHBsYXRlX2lkX29ubHlfcmVnZXggPC0gIls6YWxudW06XSsoPz0oWzphbG51bTpdXFwtWzphbG51bTpdKyl7MX0kKSIKbWlybmFfbGV1a19mcmFjX3NoYXJlZF9pZHMgPC0gbWlybmFfbGV1a19mcmFjX3NoYXJlZF9pZHMgJT4lCiAgICBmaWx0ZXIoKHN0cl9leHRyYWN0KGlkX21pcm5hLCBwb3J0aW9uX2lkX21pbnVzX2FuYWx5dGVfcmVnZXgpCiAgICAgICAgICAgID09IHN0cl9leHRyYWN0KGlkX2xldWtfZnJhYywgcG9ydGlvbl9pZF9taW51c19hbmFseXRlX3JlZ2V4KSkpCmBgYAoKTk9URTogc2V2ZXJhbCBzYW1wbGVzIHdlcmUgYXNzYXllZCBvbiBtdWx0aXBsZSBwbGF0ZXM7IGF2ZXJhZ2UgdGhlIGxldWtvY3l0ZSBmcmFjdGlvbiBhY3Jvc3MgdGhlc2UgYmVmb3JlIGNvbXB1dGluZyBjb3JyZWxhdGlvbnMgd2l0aCBtaVJOQQoKIyMjIENvcnJlbGF0ZSBkYXRhIGZvcm1hdHRpbmcKCmBgYHtyfQpsZXVrX2ZyYWNfY29ycl9maWxlIDwtICIuLi9kYXRhL2ludGVybWVkaWF0ZS9sZXVrX2ZyYWNfY29ycmVsYXRlc19mb3JfbWlybmEuZmVhdGhlciIKZm9yY2VfZm9ybWF0IDwtIEZBTFNFCmlmICghZmlsZS5leGlzdHMobGV1a19mcmFjX2NvcnJfZmlsZSkgfCBmb3JjZV9mb3JtYXQpIHsKICAgIGxldWtfZnJhY19jb3JyX2RmIDwtIG1pcm5hX2xldWtfZnJhY19zaGFyZWRfaWRzICU+JSAKICAgICAgICBsZWZ0X2pvaW4obGV1a19mcmFjX2RmLCBieSA9IGMoImlkX2xldWtfZnJhYyIgPSAiaWQiKSkgJT4lIAogICAgICAgIGdyb3VwX2J5KGRpc2Vhc2UsIHZpYWxfaWQpICU+JSAKICAgICAgICBzdW1tYXJpc2UobGV1a19mcmFjID0gbWVhbihsZXVrX2ZyYWMpKSAlPiUgCiAgICAgICAgdW5ncm91cCgpCiAgICAKICAgIHdyaXRlX2ZlYXRoZXIobGV1a19mcmFjX2NvcnJfZGYsIGxldWtfZnJhY19jb3JyX2ZpbGUpCn0gZWxzZSB7CiAgICBsZXVrX2ZyYWNfY29ycl9kZiA8LSByZWFkX2ZlYXRoZXIobGV1a19mcmFjX2NvcnJfZmlsZSkKfQpgYGAKCiMjIyBEaXNlYXNlLXdpc2UgY29ycmVsYXRpb25zCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQptaXJuYV9sZXVrX2ZyYWNfY29ycl9maWxlIDwtICIuLi9yZXN1bHRzL21pcm5hX2xldWtfZnJhY19jb3JyZWxhdGlvbi5mZWF0aGVyIgpmb3JjZV9jb21wdXRlIDwtIEZBTFNFCmlmICghZmlsZS5leGlzdHMobWlybmFfbGV1a19mcmFjX2NvcnJfZmlsZSkgfCBmb3JjZV9jb21wdXRlKSB7CiAgICAKICAgICMgc2tpcCBpbW11bmUgY2VsbCBjYW5jZXJzPwogICAgZF9saXN0IDwtIG1pcm5hX3NhbXBsZV9wdF9kZiAlPiUgCiAgICAgICAgIyBmaWx0ZXIoKCFEaXNlYXNlICVpbiUgYygiTEFNTCIsICJUSFlNIiwgIkRMQkMiKSkpICU+JSAKICAgICAgICBmaWx0ZXIoRGlzZWFzZSAlaW4lIGxldWtfZnJhY19jb3JyX2RmJGRpc2Vhc2UpICU+JSAKICAgICAgICBkaXN0aW5jdChEaXNlYXNlKSAlPiUgCiAgICAgICAgbXV0YXRlKERpc2Vhc2UgPSBhcy5jaGFyYWN0ZXIoRGlzZWFzZSkpICU+JSAKICAgICAgICAuJERpc2Vhc2UgJT4lCiAgICAgICAgc2V0X25hbWVzKC4pICU+JSAKICAgICAgICBhcy5saXN0KCkKICAgIAogICAgY29ycl9kZl9saXN0IDwtIG1jbGFwcGx5KGRfbGlzdCwgbWMuY29yZXMgPSA0LCBmdW5jdGlvbihkKSB7CiAgICAgICAgc2FtcGxlc19kIDwtIG1pcm5hX3NhbXBsZV9wdF9kZiAlPiUKICAgICAgICAgICAgZmlsdGVyKERpc2Vhc2UgPT0gZCkgJT4lCiAgICAgICAgICAgIHNlbGVjdCh2aWFsX2lkKSAlPiUKICAgICAgICAgICAgZmxhdHRlbl9jaHIoKSAlPiUKICAgICAgICAgICAgaW50ZXJzZWN0KGxldWtfZnJhY19jb3JyX2RmJHZpYWxfaWQpCgogICAgICAgIHNvdXJjZV9kZiA8LSBtaXJuYV9jb3JyX2RmICU+JQogICAgICAgICAgICBzZXRfbmFtZXMoc3RyX3JlcGxhY2UobmFtZXMoLiksICIoXFwtWzphbG51bTpdKyl7M30kIiwgIiIpKSAlPiUKICAgICAgICAgICAgc2VsZWN0KG9uZV9vZihjKCJHZW5lcyIsIHNhbXBsZXNfZCkpKSAlPiUKICAgICAgICAgICAgZHBseXI6OnJlbmFtZShtaXJuYSA9IEdlbmVzKSAlPiUKICAgICAgICAgICAgZ2F0aGVyKHNhbXBsZSwgeCwgLW1pcm5hKSAlPiUgCiAgICAgICAgICAgIGZpbHRlcighaXMubmEoeCkpCiAgICAgICAgCiAgICAgICAgdGFyZ2V0X2RmIDwtIGxldWtfZnJhY19jb3JyX2RmICU+JQogICAgICAgICAgICBmaWx0ZXIodmlhbF9pZCAlaW4lIHNhbXBsZXNfZCkgJT4lIAogICAgICAgICAgICBkcGx5cjo6cmVuYW1lKHNhbXBsZSA9IHZpYWxfaWQsIHggPSBsZXVrX2ZyYWMpICU+JSAKICAgICAgICAgICAgbXV0YXRlKGNvcnJlbGF0ZSA9ICJsZXVrX2ZyYWMiKQogICAgICAgIAogICAgICAgIGNvcnJfZGYgPC0gaW5uZXJfam9pbihzb3VyY2VfZGYsIHRhcmdldF9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAic2FtcGxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4ID0gYygiX3NvdXJjZSIsICJfdGFyZ2V0IikpICU+JQogICAgICAgICAgICBncm91cF9ieShtaXJuYSwgY29ycmVsYXRlKSAlPiUKICAgICAgICAgICAgZG8odGlkeShjb3IudGVzdCguJHhfc291cmNlLCAuJHhfdGFyZ2V0LCBtZXRob2QgPSAic3BlYXJtYW4iKSkpICU+JSAKICAgICAgICAgICAgdW5ncm91cCgpCiAgICAgICAgY29ycl9kZltbInAuYWRqdXN0Il1dIDwtIHAuYWRqdXN0KGNvcnJfZGYkcC52YWx1ZSwgbWV0aG9kID0gIkJIIikKICAgICAgICByZXR1cm4oY29ycl9kZikKICAgIH0pCiAgICAKICAgIG1pcm5hX2xldWtfZnJhY19jb3JyX2RmIDwtIGJpbmRfcm93cyhjb3JyX2RmX2xpc3QsIC5pZCA9ICJkaXNlYXNlIikgJT4lIAogICAgICAgIG11dGF0ZShjb3JyZWxhdGVfdHlwZSA9ICJsZXVrb2N5dGUgZnJhY3Rpb24iKQogICAgd3JpdGVfZmVhdGhlcihtaXJuYV9sZXVrX2ZyYWNfY29ycl9kZiwgbWlybmFfbGV1a19mcmFjX2NvcnJfZmlsZSkKfSBlbHNlIHsKICAgIG1pcm5hX2xldWtfZnJhY19jb3JyX2RmIDwtIHJlYWRfZmVhdGhlcihtaXJuYV9sZXVrX2ZyYWNfY29ycl9maWxlKQp9CmBgYAoKXCAgCgojIyBDSUJFUlNPUlQgZnJhY3Rpb24KCiMjIyBSZXRyaWV2ZS9sb2FkIGRhdGEKCkNlbGx1bGFyIENvbnRlbnQ6IHN5bjQ5OTE2MTEKc3luODAyNDU2NSAoIlRDR0EuY2x1c3Rlci1ieS1DSUJFUlNPUlQtcmVsYXRpdmUudHN2IikKCm9yLi4uPwoKc3luNzMzNzIyMSAoIlRDR0EuS2FsbGlzdG8uZnVsbElEcy5jaWJlcnNvcnQucmVsYXRpdmUudHN2IikKCmBgYHtyfQojIGNpYmVyX2ZyYWNfZmlsZSA8LSAiLi4vZGF0YS90Y2dhL1RDR0EuY2x1c3Rlci1ieS1DSUJFUlNPUlQtcmVsYXRpdmUudHN2IgpjaWJlcl9mcmFjX2ZpbGUgPC0gIi4uL2RhdGEvdGNnYS9UQ0dBLkthbGxpc3RvLmZ1bGxJRHMuY2liZXJzb3J0LnJlbGF0aXZlLnRzdiIKCmNpYmVyX2ZyYWNfZGYgPC0gcmVhZF90c3YoY2liZXJfZnJhY19maWxlKQpgYGAKCiMjIyBTYW1wbGUgZmlsdGVyaW5nCgpgYGB7cn0KY2liZXJfZnJhY19kZiA8LSBjaWJlcl9mcmFjX2RmICU+JQogICAgbXV0YXRlKGlkID0gc3RyX3JlcGxhY2VfYWxsKFNhbXBsZUlELCAiXFwuIiwgIlxcLSIpKSAlPiUgCiAgICBmaWx0ZXIoIShpZCAlaW4lIGV4Y2x1ZGVfc2FtcGxlcyRhbGlxdW90X2JhcmNvZGUpKQpgYGAKCiMjIyBTYW1wbGUgbWF0Y2hpbmcKCklkZW50aWZ5IG1hdGNoZWQgc2FtcGxlcyBiZXR3ZWVuIG1pUk5BIGFuZCBDSUJFUlNPUlQgZnJhY3Rpb24uCgpgYGB7cn0KY2liZXJfZnJhY19pZHMgPC0gY2liZXJfZnJhY19kZiAlPiUgCiAgICBzZWxlY3QoaWQpICU+JQogICAgbXV0YXRlKHZpYWxfaWQgPSBzdHJfcmVwbGFjZShpZCwgIihcXC1bOmFsbnVtOl0rKXszfSQiLCAiIikpICU+JSAKICAgIGFycmFuZ2UoKQoKIyBvbmx5IGtlZXAgc2FtcGxlcyB3aXRoIG1hdGNoZWQgdmlhbCBJRCBBTkQgcG9ydGlvbiBudW1iZXIKbWlybmFfY2liZXJfZnJhY19zaGFyZWRfaWRzIDwtIGlubmVyX2pvaW4obWlybmFfaWRzLCBjaWJlcl9mcmFjX2lkcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJ2aWFsX2lkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWZmaXggPSBjKCJfbWlybmEiLCAiX2NpYmVyX2ZyYWMiKSkgJT4lIAogICAgZGlzdGluY3QoKSAlPiUgCiAgICBmaWx0ZXIoKHN0cl9leHRyYWN0KGlkX21pcm5hLCBwb3J0aW9uX2lkX21pbnVzX2FuYWx5dGVfcmVnZXgpIAogICAgICAgICAgICA9PSBzdHJfZXh0cmFjdChpZF9jaWJlcl9mcmFjLCBwb3J0aW9uX2lkX21pbnVzX2FuYWx5dGVfcmVnZXgpKSkKYGBgCgpOT1RFOiBzZXZlcmFsIHNhbXBsZXMgd2VyZSBhc3NheWVkIG9uIG11bHRpcGxlIHBsYXRlczsgYXZlcmFnZSB0aGUgQ0lCRVJTT1JUIGZyYWN0aW9uIGFjcm9zcyB0aGVzZSBiZWZvcmUgY29tcHV0aW5nIGNvcnJlbGF0aW9ucyB3aXRoIG1pUk5BCgojIyMgQ29ycmVsYXRlIGRhdGEgZm9ybWF0dGluZwoKYGBge3J9CmNpYmVyX2ZyYWNfY29ycl9maWxlIDwtICIuLi9kYXRhL2ludGVybWVkaWF0ZS9jaWJlcl9mcmFjX2NvcnJlbGF0ZXNfZm9yX21pcm5hLmZlYXRoZXIiCmZvcmNlX2Zvcm1hdCA8LSBGQUxTRQppZiAoIWZpbGUuZXhpc3RzKGNpYmVyX2ZyYWNfY29ycl9maWxlKSB8IGZvcmNlX2Zvcm1hdCkgewogICAgY2liZXJfZnJhY19jb3JyX2RmIDwtIG1pcm5hX2NpYmVyX2ZyYWNfc2hhcmVkX2lkcyAlPiUKICAgICAgICBsZWZ0X2pvaW4oY2liZXJfZnJhY19kZiwgYnkgPSBjKCJpZF9jaWJlcl9mcmFjIiA9ICJpZCIpKSAlPiUgCiAgICAgICAgc2VsZWN0KC1pZF9taXJuYSwgLWlkX2NpYmVyX2ZyYWMsIAogICAgICAgICAgICAgICAtU2FtcGxlSUQsIC1QLnZhbHVlLCAtQ29ycmVsYXRpb24sIC1STVNFKSAlPiUgCiAgICAgICAgZ3JvdXBfYnkoQ2FuY2VyVHlwZSwgdmlhbF9pZCkgJT4lCiAgICAgICAgc3VtbWFyaXNlX2VhY2goZnVucyhtZWFuKSkgJT4lIAogICAgICAgIHVuZ3JvdXAoKQogICAgCiAgICB3cml0ZV9mZWF0aGVyKGNpYmVyX2ZyYWNfY29ycl9kZiwgY2liZXJfZnJhY19jb3JyX2ZpbGUpCn0gZWxzZSB7CiAgICBjaWJlcl9mcmFjX2NvcnJfZGYgPC0gcmVhZF9mZWF0aGVyKGNpYmVyX2ZyYWNfY29ycl9maWxlKQp9CmBgYAoKIyMjIERpc2Vhc2Utd2lzZSBjb3JyZWxhdGlvbnMKCmBgYHtyIHdhcm5pbmc9RkFMU0V9Cm1pcm5hX2NpYmVyX2ZyYWNfY29ycl9maWxlIDwtICIuLi9yZXN1bHRzL21pcm5hX2NpYmVyX2ZyYWNfY29ycmVsYXRpb24uZmVhdGhlciIKZm9yY2VfY29tcHV0ZSA8LSBGQUxTRQppZiAoIWZpbGUuZXhpc3RzKG1pcm5hX2NpYmVyX2ZyYWNfY29ycl9maWxlKSB8IGZvcmNlX2NvbXB1dGUpIHsKICAgIAogICAgIyBza2lwIGltbXVuZSBjZWxsIGNhbmNlcnM/CiAgICBkX2xpc3QgPC0gbWlybmFfc2FtcGxlX3B0X2RmICU+JQogICAgICAgICMgZmlsdGVyKCghRGlzZWFzZSAlaW4lIGMoIkxBTUwiLCAiVEhZTSIsICJETEJDIikpKSAlPiUgCiAgICAgICAgZmlsdGVyKERpc2Vhc2UgJWluJSBjaWJlcl9mcmFjX2NvcnJfZGYkQ2FuY2VyVHlwZSkgJT4lIAogICAgICAgIGRpc3RpbmN0KERpc2Vhc2UpICU+JSAKICAgICAgICBtdXRhdGUoRGlzZWFzZSA9IGFzLmNoYXJhY3RlcihEaXNlYXNlKSkgJT4lIAogICAgICAgIC4kRGlzZWFzZSAlPiUKICAgICAgICBzZXRfbmFtZXMoLikgJT4lIAogICAgICAgIGFzLmxpc3QoKQogICAgCiAgICBjb3JyX2RmX2xpc3QgPC0gbWNsYXBwbHkoZF9saXN0LCBtYy5jb3JlcyA9IDQsIGZ1bmN0aW9uKGQpIHsKICAgICAgICBzYW1wbGVzX2QgPC0gbWlybmFfc2FtcGxlX3B0X2RmICU+JQogICAgICAgICAgICBmaWx0ZXIoRGlzZWFzZSA9PSBkKSAlPiUKICAgICAgICAgICAgc2VsZWN0KHZpYWxfaWQpICU+JQogICAgICAgICAgICBmbGF0dGVuX2NocigpICU+JQogICAgICAgICAgICBpbnRlcnNlY3QoY2liZXJfZnJhY19jb3JyX2RmJHZpYWxfaWQpCgogICAgICAgIHNvdXJjZV9kZiA8LSBtaXJuYV9jb3JyX2RmICU+JQogICAgICAgICAgICBzZXRfbmFtZXMoc3RyX3JlcGxhY2UobmFtZXMoLiksICIoXFwtWzphbG51bTpdKyl7M30kIiwgIiIpKSAlPiUKICAgICAgICAgICAgc2VsZWN0KG9uZV9vZihjKCJHZW5lcyIsIHNhbXBsZXNfZCkpKSAlPiUKICAgICAgICAgICAgZHBseXI6OnJlbmFtZShtaXJuYSA9IEdlbmVzKSAlPiUKICAgICAgICAgICAgZ2F0aGVyKHNhbXBsZSwgeCwgLW1pcm5hKSAlPiUgCiAgICAgICAgICAgIGZpbHRlcighaXMubmEoeCkpCgogICAgICAgIHRhcmdldF9kZiA8LSBjaWJlcl9mcmFjX2NvcnJfZGYgJT4lCiAgICAgICAgICAgIGZpbHRlcih2aWFsX2lkICVpbiUgc2FtcGxlc19kKSAlPiUgCiAgICAgICAgICAgIHNlbGVjdCgtQ2FuY2VyVHlwZSkgJT4lIAogICAgICAgICAgICBnYXRoZXIoY29ycmVsYXRlLCB4LCAtdmlhbF9pZCkgJT4lIAogICAgICAgICAgICBkcGx5cjo6cmVuYW1lKHNhbXBsZSA9IHZpYWxfaWQpCiAgICAgICAgCiAgICAgICAgY29ycl9kZiA8LSBpbm5lcl9qb2luKHNvdXJjZV9kZiwgdGFyZ2V0X2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJzYW1wbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWZmaXggPSBjKCJfc291cmNlIiwgIl90YXJnZXQiKSkgJT4lCiAgICAgICAgICAgIGdyb3VwX2J5KG1pcm5hLCBjb3JyZWxhdGUpICU+JQogICAgICAgICAgICBkbyh0aWR5KGNvci50ZXN0KC4keF9zb3VyY2UsIC4keF90YXJnZXQsIG1ldGhvZCA9ICJzcGVhcm1hbiIpKSkgJT4lIAogICAgICAgICAgICB1bmdyb3VwKCkKICAgICAgICBjb3JyX2RmW1sicC5hZGp1c3QiXV0gPC0gcC5hZGp1c3QoY29ycl9kZiRwLnZhbHVlLCBtZXRob2QgPSAiQkgiKQogICAgICAgIHJldHVybihjb3JyX2RmKQogICAgfSkKICAgIAogICAgbWlybmFfY2liZXJfZnJhY19jb3JyX2RmIDwtIGJpbmRfcm93cyhjb3JyX2RmX2xpc3QsIC5pZCA9ICJkaXNlYXNlIikgJT4lIAogICAgICAgIG11dGF0ZShjb3JyZWxhdGVfdHlwZSA9ICJDSUJFUlNPUlQgZnJhY3Rpb24iKQogICAgd3JpdGVfZmVhdGhlcihtaXJuYV9jaWJlcl9mcmFjX2NvcnJfZGYsIG1pcm5hX2NpYmVyX2ZyYWNfY29ycl9maWxlKQp9IGVsc2UgewogICAgbWlybmFfY2liZXJfZnJhY19jb3JyX2RmIDwtIHJlYWRfZmVhdGhlcihtaXJuYV9jaWJlcl9mcmFjX2NvcnJfZmlsZSkKfQpgYGAKClwgIAoKIyMgTXV0YXRpb24gbG9hZAoKIyMjIFJldHJpZXZlL2xvYWQgZGF0YQoKTXV0YXRpb24gTG9hZDogc3luNTcwNjgyNwpzeW43OTk0NzI4ICgibXV0YXRpb24tbG9hZCIpCgoqIGV4Y2x1ZGUgTEFNTD8KCmBgYHtyfQptdXRfbG9hZF9maWxlIDwtICIuLi9kYXRhL3RjZ2EvbXV0YXRpb24tbG9hZC12aWFsLnR4dCIKbXV0X2xvYWRfZGYgPC0gcmVhZF90c3YobXV0X2xvYWRfZmlsZSkKYGBgCgojIyMgU2FtcGxlIGZpbHRlcmluZwoKQ2FuJ3QgZmlsdGVyIGFsaXF1b3RzLCBhcyBkYXRhIG9ubHkgaW5jbHVkZXMgSURzIHNwZWNpZmljIHRvIHRoZSBsZXZlbCBvZiB2aWFsCgojIyMgU2FtcGxlIG1hdGNoaW5nCgpJZGVudGlmeSBtYXRjaGVkIHNhbXBsZXMgYmV0d2VlbiBtaVJOQSBhbmQgbXV0YXRpb25hbCBsb2FkLgoKYGBge3J9Cm11dF9sb2FkX2lkcyA8LSBtdXRfbG9hZF9kZiAlPiUgCiAgICBzZWxlY3QoaWQgPSBUdW1vcl9TYW1wbGVfSURfVmlhbCkgJT4lIAogICAgbXV0YXRlKHZpYWxfaWQgPSBpZCkgJT4lCiAgICBhcnJhbmdlKCkKCm1pcm5hX211dF9sb2FkX3NoYXJlZF9pZHMgPC0gaW5uZXJfam9pbihtaXJuYV9pZHMsIG11dF9sb2FkX2lkcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJ2aWFsX2lkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1ZmZpeCA9IGMoIl9taXJuYSIsICJfbXV0X2xvYWQiKSkKYGBgCgojIyMgQ29ycmVsYXRlIGRhdGEgZm9ybWF0dGluZwoKYGBge3J9Cm11dF9sb2FkX2NvcnJfZmlsZSA8LSAiLi4vZGF0YS9pbnRlcm1lZGlhdGUvbXV0X2xvYWRfY29ycmVsYXRlc19mb3JfbWlybmEuZmVhdGhlciIKZm9yY2VfZm9ybWF0IDwtIEZBTFNFCmlmICghZmlsZS5leGlzdHMobXV0X2xvYWRfY29ycl9maWxlKSB8IGZvcmNlX2Zvcm1hdCkgewogICAgbXV0X2xvYWRfY29ycl9kZiA8LSBtaXJuYV9tdXRfbG9hZF9zaGFyZWRfaWRzICU+JSAKICAgICAgICBsZWZ0X2pvaW4obXV0X2xvYWRfZGYsIGJ5ID0gYygiaWRfbXV0X2xvYWQiID0gIlR1bW9yX1NhbXBsZV9JRF9WaWFsIikpCiAgICAKICAgIHdyaXRlX2ZlYXRoZXIobXV0X2xvYWRfY29ycl9kZiwgbXV0X2xvYWRfY29ycl9maWxlKQp9IGVsc2UgewogICAgbXV0X2xvYWRfY29ycl9kZiA8LSByZWFkX2ZlYXRoZXIobXV0X2xvYWRfY29ycl9maWxlKQp9CmBgYAoKIyMjIERpc2Vhc2Utd2lzZSBjb3JyZWxhdGlvbnMKCmBgYHtyIHdhcm5pbmc9RkFMU0V9Cm1pcm5hX211dF9sb2FkX2NvcnJfZmlsZSA8LSAiLi4vcmVzdWx0cy9taXJuYV9tdXRfbG9hZF9jb3JyZWxhdGlvbi5mZWF0aGVyIgpmb3JjZV9jb21wdXRlIDwtIEZBTFNFCmlmICghZmlsZS5leGlzdHMobWlybmFfbXV0X2xvYWRfY29ycl9maWxlKSB8IGZvcmNlX2NvbXB1dGUpIHsKICAgIAogICAgIyBza2lwIGltbXVuZSBjZWxsIGNhbmNlcnM/CiAgICBkX2xpc3QgPC0gbWlybmFfc2FtcGxlX3B0X2RmICU+JQogICAgICAgICMgZmlsdGVyKCghRGlzZWFzZSAlaW4lIGMoIkxBTUwiLCAiVEhZTSIsICJETEJDIikpKSAlPiUKICAgICAgICBmaWx0ZXIoRGlzZWFzZSAlaW4lIG11dF9sb2FkX2NvcnJfZGYkY29ob3J0KSAlPiUgCiAgICAgICAgZGlzdGluY3QoRGlzZWFzZSkgJT4lIAogICAgICAgIG11dGF0ZShEaXNlYXNlID0gYXMuY2hhcmFjdGVyKERpc2Vhc2UpKSAlPiUgCiAgICAgICAgLiREaXNlYXNlICU+JQogICAgICAgIHNldF9uYW1lcyguKSAlPiUgCiAgICAgICAgYXMubGlzdCgpCiAgICAKICAgIGNvcnJfZGZfbGlzdCA8LSBtY2xhcHBseShkX2xpc3QsIG1jLmNvcmVzID0gNCwgZnVuY3Rpb24oZCkgewogICAgICAgIHNhbXBsZXNfZCA8LSBtaXJuYV9zYW1wbGVfcHRfZGYgJT4lCiAgICAgICAgICAgIGZpbHRlcihEaXNlYXNlID09IGQpICU+JQogICAgICAgICAgICBzZWxlY3QodmlhbF9pZCkgJT4lCiAgICAgICAgICAgIGZsYXR0ZW5fY2hyKCkgJT4lCiAgICAgICAgICAgIGludGVyc2VjdChtdXRfbG9hZF9jb3JyX2RmJHZpYWxfaWQpCgogICAgICAgIHNvdXJjZV9kZiA8LSBtaXJuYV9jb3JyX2RmICU+JQogICAgICAgICAgICBzZXRfbmFtZXMoc3RyX3JlcGxhY2UobmFtZXMoLiksICIoXFwtWzphbG51bTpdKyl7M30kIiwgIiIpKSAlPiUKICAgICAgICAgICAgc2VsZWN0KG9uZV9vZihjKCJHZW5lcyIsIHNhbXBsZXNfZCkpKSAlPiUKICAgICAgICAgICAgZHBseXI6OnJlbmFtZShtaXJuYSA9IEdlbmVzKSAlPiUKICAgICAgICAgICAgZ2F0aGVyKHNhbXBsZSwgeCwgLW1pcm5hKSAlPiUgCiAgICAgICAgICAgIGZpbHRlcighaXMubmEoeCkpCgogICAgICAgIHRhcmdldF9kZiA8LSBtdXRfbG9hZF9jb3JyX2RmICU+JQogICAgICAgICAgICBmaWx0ZXIodmlhbF9pZCAlaW4lIHNhbXBsZXNfZCkgJT4lIAogICAgICAgICAgICBzZWxlY3QoLWlkX21pcm5hLCAtaWRfbXV0X2xvYWQsIC1jb2hvcnQsIC1QYXRpZW50X0lELCAKICAgICAgICAgICAgICAgICAgIC1UdW1vcl9TYW1wbGVfSUQpICU+JSAKICAgICAgICAgICAgZ2F0aGVyKGNvcnJlbGF0ZSwgeCwgLXZpYWxfaWQpICU+JSAKICAgICAgICAgICAgZHBseXI6OnJlbmFtZShzYW1wbGUgPSB2aWFsX2lkKQogICAgICAgIAogICAgICAgIGNvcnJfZGYgPC0gaW5uZXJfam9pbihzb3VyY2VfZGYsIHRhcmdldF9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAic2FtcGxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4ID0gYygiX3NvdXJjZSIsICJfdGFyZ2V0IikpICU+JQogICAgICAgICAgICBncm91cF9ieShtaXJuYSwgY29ycmVsYXRlKSAlPiUKICAgICAgICAgICAgZG8odGlkeShjb3IudGVzdCguJHhfc291cmNlLCAuJHhfdGFyZ2V0LCBtZXRob2QgPSAic3BlYXJtYW4iKSkpICU+JSAKICAgICAgICAgICAgdW5ncm91cCgpCiAgICAgICAgY29ycl9kZltbInAuYWRqdXN0Il1dIDwtIHAuYWRqdXN0KGNvcnJfZGYkcC52YWx1ZSwgbWV0aG9kID0gIkJIIikKICAgICAgICByZXR1cm4oY29ycl9kZikKICAgIH0pCiAgICAKICAgIG1pcm5hX211dF9sb2FkX2NvcnJfZGYgPC0gYmluZF9yb3dzKGNvcnJfZGZfbGlzdCwgLmlkID0gImRpc2Vhc2UiKSAlPiUgCiAgICAgICAgbXV0YXRlKGNvcnJlbGF0ZV90eXBlID0gIm11dGF0aW9uIGxvYWQiKQogICAgd3JpdGVfZmVhdGhlcihtaXJuYV9tdXRfbG9hZF9jb3JyX2RmLCBtaXJuYV9tdXRfbG9hZF9jb3JyX2ZpbGUpCn0gZWxzZSB7CiAgICBtaXJuYV9tdXRfbG9hZF9jb3JyX2RmIDwtIHJlYWRfZmVhdGhlcihtaXJuYV9tdXRfbG9hZF9jb3JyX2ZpbGUpCn0KYGBgCgpcICAKCiMjIFRDUi9CQ1IgZGl2ZXJzaXR5CgojIyMgUmV0cmlldmUvbG9hZCBkYXRhCgpUIENlbGwgUmVjZXB0b3IgLyBCcm93biBldCBhbDogc3luNTg3NjQ4OApzeW43MDYzNDIyICgibWl0Y3Jfc2FtcGxlU3RhdGlzdGljc18yMDE2MDcxNC50c3YiKQoKYGBge3J9CnRjcl9kaXZfZmlsZSA8LSAiLi4vZGF0YS90Y2dhL21pdGNyX3NhbXBsZVN0YXRpc3RpY3NfMjAxNjA3MTQudHN2IgoKdGNyX2Rpdl9kZiA8LSByZWFkX3Rzdih0Y3JfZGl2X2ZpbGUpCmBgYAoKIyMjIFNhbXBsZSBmaWx0ZXJpbmcKCmBgYHtyfQp0Y3JfZHZfZGYgPC0gdGNyX2Rpdl9kZiAlPiUKICAgIGZpbHRlcighKEFsaXF1b3RCYXJjb2RlICVpbiUgZXhjbHVkZV9zYW1wbGVzJGFsaXF1b3RfYmFyY29kZSkpCmBgYAoKIyMjIFNhbXBsZSBtYXRjaGluZwoKSWRlbnRpZnkgbWF0Y2hlZCBzYW1wbGVzIGJldHdlZW4gbWlSTkEgYW5kIENJQkVSU09SVCBmcmFjdGlvbi4KCmBgYHtyfQp0Y3JfZGl2X2lkcyA8LSB0Y3JfZGl2X2RmICU+JSAKICAgIHNlbGVjdChpZCA9IEFsaXF1b3RCYXJjb2RlLCB2aWFsX2lkID0gU2FtcGxlQmFyY29kZSkgJT4lIAogICAgYXJyYW5nZSgpCgojIG9ubHkga2VlcCBzYW1wbGVzIHdpdGggbWF0Y2hlZCB2aWFsIElEIEFORCBwb3J0aW9uIG51bWJlcgptaXJuYV90Y3JfZGl2X3NoYXJlZF9pZHMgPC0gaW5uZXJfam9pbihtaXJuYV9pZHMsIHRjcl9kaXZfaWRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJ2aWFsX2lkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4ID0gYygiX21pcm5hIiwgIl90Y3JfZGl2IikpICU+JSAKICAgIGRpc3RpbmN0KCkgJT4lIAogICAgZmlsdGVyKChzdHJfZXh0cmFjdChpZF9taXJuYSwgcG9ydGlvbl9pZF9taW51c19hbmFseXRlX3JlZ2V4KSAKICAgICAgICAgICAgPT0gc3RyX2V4dHJhY3QoaWRfdGNyX2RpdiwgcG9ydGlvbl9pZF9taW51c19hbmFseXRlX3JlZ2V4KSkpCmBgYAoKTk9URTogc2V2ZXJhbCBzYW1wbGVzIHdlcmUgYXNzYXllZCBvbiBtdWx0aXBsZSBwbGF0ZXM7IGF2ZXJhZ2UgdGhlIFRDUiBkaXZlcnNpdHkgYWNyb3NzIHRoZXNlIGJlZm9yZSBjb21wdXRpbmcgY29ycmVsYXRpb25zIHdpdGggbWlSTkEKCiMjIyBDb3JyZWxhdGUgZGF0YSBmb3JtYXR0aW5nCgpTb21lIHNhbXBsZXMgKGBBbGlxdW90QmFyY29kZWApIGhhdmUgMiB2YWx1ZXMgZm9yIFNoYW5ub24gZW50cm9weSwgYXBwYXJlbnRseSBjb3JyZXNwb25kaW5nIHRvIHNlcGFyYXRlIGFuYWx5c2VzIG9uIENHSHViLiBJJ2xsIGdvIGFoZWFkIGFuZCBhdmVyYWdlIHRoZXNlIHZhbHVlcyBwZXIgYWxpcXVvdCwgcHJpb3IgdG8gYXZlcmFnaW5nIFNoYW5ub24gdmFsdWVzIHBlciB2aWFsIElELiBBcyBhIHNtYWxsIG1lYXN1cmUgb2YgUUMsIEknbGwgYWxzbyBvbmx5IGtlZXAgb2JzZXJ2YXRpb25zIHdpdGggYXQgbGVhc3QgMSBUQ1JBIHJlYWQgb3IgMSBUQ1JCIHJlYWQuCgpgYGB7cn0KdGNyX2Rpdl9jb3JyX2ZpbGUgPC0gIi4uL2RhdGEvaW50ZXJtZWRpYXRlL3Rjcl9kaXZfY29ycmVsYXRlc19mb3JfbWlybmEuZmVhdGhlciIKZm9yY2VfZm9ybWF0IDwtIEZBTFNFCmlmICghZmlsZS5leGlzdHModGNyX2Rpdl9jb3JyX2ZpbGUpIHwgZm9yY2VfZm9ybWF0KSB7CiAgICB0Y3JfZGl2X2NvcnJfZGYgPC0gbWlybmFfdGNyX2Rpdl9zaGFyZWRfaWRzICU+JQogICAgICAgIGxlZnRfam9pbih0Y3JfZGl2X2RmLCBieSA9IGMoImlkX3Rjcl9kaXYiID0gIkFsaXF1b3RCYXJjb2RlIikpICU+JQogICAgICAgIHNlbGVjdCgtQ0dIdWJfYW5hbHlzaXNfaWQpICU+JSAKICAgICAgICBkaXN0aW5jdCgpICU+JSAKICAgICAgICBmaWx0ZXIodG90VENSYV9yZWFkcyA+IDAgfCB0b3RUQ1JiX3JlYWRzID4gMCkgJT4lCiAgICAgICAgZ3JvdXBfYnkoU3R1ZHksIHZpYWxfaWQsIGlkX3Rjcl9kaXYpICU+JQogICAgICAgIHN1bW1hcml6ZShzaGFubm9uID0gbWVhbihzaGFubm9uKSkgJT4lCiAgICAgICAgZ3JvdXBfYnkoU3R1ZHksIHZpYWxfaWQpICU+JQogICAgICAgIHN1bW1hcmlzZShzaGFubm9uID0gbWVhbihzaGFubm9uKSkgJT4lIAogICAgICAgIHVuZ3JvdXAoKQoKICAgIHdyaXRlX2ZlYXRoZXIodGNyX2Rpdl9jb3JyX2RmLCB0Y3JfZGl2X2NvcnJfZmlsZSkKfSBlbHNlIHsKICAgIHRjcl9kaXZfY29ycl9kZiA8LSByZWFkX2ZlYXRoZXIodGNyX2Rpdl9jb3JyX2ZpbGUpCn0KYGBgCgojIyMgRGlzZWFzZS13aXNlIGNvcnJlbGF0aW9ucwoKYGBge3Igd2FybmluZz1GQUxTRX0KbWlybmFfdGNyX2Rpdl9jb3JyX2ZpbGUgPC0gIi4uL3Jlc3VsdHMvbWlybmFfdGNyX2Rpdl9jb3JyZWxhdGlvbi5mZWF0aGVyIgpmb3JjZV9jb21wdXRlIDwtIEZBTFNFCmlmICghZmlsZS5leGlzdHMobWlybmFfdGNyX2Rpdl9jb3JyX2ZpbGUpIHwgZm9yY2VfY29tcHV0ZSkgewogICAgCiAgICAjIHNraXAgaW1tdW5lIGNlbGwgY2FuY2VycwogICAgZF9saXN0IDwtIG1pcm5hX3NhbXBsZV9wdF9kZiAlPiUKICAgICAgICAjIGZpbHRlcigoIURpc2Vhc2UgJWluJSBjKCJMQU1MIiwgIlRIWU0iLCAiRExCQyIpKSkgJT4lIAogICAgICAgIGZpbHRlcihEaXNlYXNlICVpbiUgdGNyX2Rpdl9jb3JyX2RmJFN0dWR5KSAlPiUgCiAgICAgICAgZGlzdGluY3QoRGlzZWFzZSkgJT4lIAogICAgICAgIG11dGF0ZShEaXNlYXNlID0gYXMuY2hhcmFjdGVyKERpc2Vhc2UpKSAlPiUgCiAgICAgICAgLiREaXNlYXNlICU+JQogICAgICAgIHNldF9uYW1lcyguKSAlPiUgCiAgICAgICAgYXMubGlzdCgpCiAgICAKICAgIGNvcnJfZGZfbGlzdCA8LSBtY2xhcHBseShkX2xpc3QsIG1jLmNvcmVzID0gNCwgZnVuY3Rpb24oZCkgewogICAgICAgIHNhbXBsZXNfZCA8LSBtaXJuYV9zYW1wbGVfcHRfZGYgJT4lCiAgICAgICAgICAgIGZpbHRlcihEaXNlYXNlID09IGQpICU+JQogICAgICAgICAgICBzZWxlY3QodmlhbF9pZCkgJT4lCiAgICAgICAgICAgIGZsYXR0ZW5fY2hyKCkgJT4lCiAgICAgICAgICAgIGludGVyc2VjdCh0Y3JfZGl2X2NvcnJfZGYkdmlhbF9pZCkKCiAgICAgICAgc291cmNlX2RmIDwtIG1pcm5hX2NvcnJfZGYgJT4lCiAgICAgICAgICAgIHNldF9uYW1lcyhzdHJfcmVwbGFjZShuYW1lcyguKSwgIihcXC1bOmFsbnVtOl0rKXszfSQiLCAiIikpICU+JQogICAgICAgICAgICBzZWxlY3Qob25lX29mKGMoIkdlbmVzIiwgc2FtcGxlc19kKSkpICU+JQogICAgICAgICAgICBkcGx5cjo6cmVuYW1lKG1pcm5hID0gR2VuZXMpICU+JQogICAgICAgICAgICBnYXRoZXIoc2FtcGxlLCB4LCAtbWlybmEpICU+JSAKICAgICAgICAgICAgZmlsdGVyKCFpcy5uYSh4KSkKCiAgICAgICAgdGFyZ2V0X2RmIDwtIHRjcl9kaXZfY29ycl9kZiAlPiUKICAgICAgICAgICAgZmlsdGVyKHZpYWxfaWQgJWluJSBzYW1wbGVzX2QpICU+JSAKICAgICAgICAgICAgc2VsZWN0KC1TdHVkeSkgJT4lIAogICAgICAgICAgICBkcGx5cjo6cmVuYW1lKHNhbXBsZSA9IHZpYWxfaWQpICU+JSAKICAgICAgICAgICAgZ2F0aGVyKGNvcnJlbGF0ZSwgeCwgLXNhbXBsZSkKICAgICAgICAKICAgICAgICBjb3JyX2RmIDwtIGlubmVyX2pvaW4oc291cmNlX2RmLCB0YXJnZXRfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gInNhbXBsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1ZmZpeCA9IGMoIl9zb3VyY2UiLCAiX3RhcmdldCIpKSAlPiUKICAgICAgICAgICAgZ3JvdXBfYnkobWlybmEsIGNvcnJlbGF0ZSkgJT4lCiAgICAgICAgICAgIGRvKHRpZHkoY29yLnRlc3QoLiR4X3NvdXJjZSwgLiR4X3RhcmdldCwgbWV0aG9kID0gInNwZWFybWFuIikpKSAlPiUgCiAgICAgICAgICAgIHVuZ3JvdXAoKQogICAgICAgIGNvcnJfZGZbWyJwLmFkanVzdCJdXSA8LSBwLmFkanVzdChjb3JyX2RmJHAudmFsdWUsIG1ldGhvZCA9ICJCSCIpCiAgICAgICAgcmV0dXJuKGNvcnJfZGYpCiAgICB9KQogICAgCiAgICBtaXJuYV90Y3JfZGl2X2NvcnJfZGYgPC0gYmluZF9yb3dzKGNvcnJfZGZfbGlzdCwgLmlkID0gImRpc2Vhc2UiKSAlPiUgCiAgICAgICAgbXV0YXRlKGNvcnJlbGF0ZV90eXBlID0gIlRDUiBkaXZlcnNpdHkiKQogICAgd3JpdGVfZmVhdGhlcihtaXJuYV90Y3JfZGl2X2NvcnJfZGYsIG1pcm5hX3Rjcl9kaXZfY29ycl9maWxlKQp9IGVsc2UgewogICAgbWlybmFfdGNyX2Rpdl9jb3JyX2RmIDwtIHJlYWRfZmVhdGhlcihtaXJuYV90Y3JfZGl2X2NvcnJfZmlsZSkKfQpgYGAKClwgIAoKIyMgR2VuZSBleHByZXNzaW9uCgorIEV4cHJlc3Npb24gb2YgIAogICAgKyBJRk5HLElGTi1nYW1tYSAgCiAgICArIFBSRjEsUGVyZm9yaW4gIAogICAgKyBHWk1BLEdyYW56eW1lIEEgIAogICAgKyBQRENEMSxQRC0xICAKICAgICsgQ0QyNzQsUEQtTDEgIAogICAgKyBQRENEMUxHMixQRC1MMiAgCiAgICArIElMMTAsSUwtMTAgIAogICAgKyBUR0ZCMSxUR0YtYmV0YSAgCiAgICArIElETzEsSURPICAKICAgICsgSExBLUEgIAoKIyMjIFJldHJpZXZlL2xvYWQgbVJOQSBkYXRhCgpCYXRjaCBlZmZlY3RzIG5vcm1hbGl6ZWQgbVJOQSBkYXRhOiBzeW40OTc2MzYzCnN5bjQ5NzYzNjkgKCJFQisrQWRqdXN0UEFOQ0FOX0lsbHVtaW5hSGlTZXFfUk5BU2VxVjIuZ2VuZUV4cC50c3YiKQpzeW40OTc2MzY2ICgiIEVCKytHZW5lRXhwQW5ub3RhdGlvbi50c3YiKQoKYGBge3IgaW5jbHVkZT1GQUxTRX0KbXJuYV9zeW5mb2xkZXIgPC0gInN5bjQ5NzYzNjMiCm1ybmFfc3luZmlsZXMgPC0gc3luYXBzZVF1ZXJ5KAogICAgc3ByaW50Zignc2VsZWN0ICogZnJvbSBmaWxlIHdoZXJlIHBhcmVudElkPT0iJXMiJywgbXJuYV9zeW5mb2xkZXIpCikKCiMgZG93bmxvYWQgZmlsZXMgYW5kIHN0b3JlIGRhdGEvcGF0aHMgaW4gbmV3IGRhdGEgZnJhbWUKbXJuYV9maWxlcyA8LSBtcm5hX3N5bmZpbGVzICU+JSAKICAgIG11dGF0ZShmaWxlX2RhdGEgPSBtYXAoZmlsZS5pZCwgZnVuY3Rpb24oc3luaWQpIHsKICAgICAgICBzeW5HZXQoc3luaWQsIGRvd25sb2FkTG9jYXRpb24gPSAiLi4vZGF0YS90Y2dhIikKICAgIH0pLAogICAgZmlsZV9wYXRoID0gbWFwX2NocihmaWxlX2RhdGEsIGdldEZpbGVMb2NhdGlvbikpCmBgYAoKU2FtcGxlIGNoYXJhY3RlcmlzdGljcyBhcmUgc3RvcmVkIGluIGEgdGFiLWRlbGltaXRlZCB0ZXh0IGZpbGUgKFN5bmFwc2UgSUQ6IGBzeW40OTc2MzY2YCkgYW5kIGNhbiBiZSBsb2FkZWQgd2l0aCBgcmVhZF90c3YoKWAuCgpgYGB7cn0KIyBsb2FkIHNhbXBsZSBkYXRhCm1ybmFfc2FtcGxlX2ZpbGUgPC0gbXJuYV9maWxlcyAlPiUgCiAgICBmaWx0ZXIoZmlsZS5pZCA9PSAic3luNDk3NjM2NiIpICU+JSAKICAgIC5bWyJmaWxlX3BhdGgiXV0KbXJuYV9zYW1wbGVfZGYgPC0gcmVhZF90c3YobXJuYV9zYW1wbGVfZmlsZSkKYGBgCgojIyMgU2FtcGxlIGZpbHRlcmluZwoKUmVtb3ZlIHNhbXBsZXMgZnJvbSBtUk5BIGRhdGFzZXQuCgpgYGB7cn0KbXJuYV9zYW1wbGVfZGYgPC0gbXJuYV9zYW1wbGVfZGYgJT4lIAogICAgZmlsdGVyKCEoU2FtcGxlSUQgJWluJSBleGNsdWRlX3NhbXBsZXMkYWxpcXVvdF9iYXJjb2RlKSkKYGBgCgojIyMgU2FtcGxlIG1hdGNoaW5nCgpJZGVudGlmeSBtYXRjaGVkIHNhbXBsZXMgYmV0d2VlbiBtaVJOQSBhbmQgbVJOQS4KCmBgYHtyfQptcm5hX2lkcyA8LSBtcm5hX3NhbXBsZV9kZiAlPiUgCiAgICBzZWxlY3QoU2FtcGxlSUQpICU+JQogICAgbXV0YXRlKHZpYWxfaWQgPSBzdHJfcmVwbGFjZShTYW1wbGVJRCwgIihcXC1bOmFsbnVtOl0rKXszfSQiLCAiIikpICU+JSAKICAgIGFycmFuZ2UoKQoKbWlybmFfbXJuYV9zaGFyZWRfaWRzIDwtIGlubmVyX2pvaW4obWlybmFfaWRzLCBtcm5hX2lkcywgYnkgPSAidmlhbF9pZCIpCgojIG9ubHkga2VlcCBzYW1wbGVzIHdpdGggbWF0Y2hlZCB2aWFsIElEIEFORCBwb3J0aW9uIG51bWJlcgptaXJuYV9tcm5hX3NoYXJlZF9pZHMgPC0gbWlybmFfbXJuYV9zaGFyZWRfaWRzICU+JQogICAgZmlsdGVyKHN0cl9leHRyYWN0KGlkLCBwb3J0aW9uX2lkX21pbnVzX2FuYWx5dGVfcmVnZXgpCiAgICAgICAgICAgPT0gc3RyX2V4dHJhY3QoU2FtcGxlSUQsIHBvcnRpb25faWRfbWludXNfYW5hbHl0ZV9yZWdleCkpCmBgYAoKbVJOQSBub3JtYWxpemVkLCBiYXRjaCBjb3JyZWN0ZWQgZXhwcmVzc2lvbiB2YWx1ZXMgZm9yIGFsbCBzYW1wbGVzIGFyZSBzdG9yZWQgYXMgYSBtYXRyaXggaW4gYSBUU1YgZmlsZSAoU3luYXBzZSBJRDogYHN5bjQ5NzYzNjlgKSBhbmQgY2FuIGJlIGxvYWRlZCB3aXRoIGByZWFkX3RzdigpYC4KCiMjIyBDb3JyZWxhdGUgZGF0YSBmb3JtYXR0aW5nCgpMaXN0IG9mIGdlbmVzIGFjY2Vzc2VkIFtoZXJlXShodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9zcHJlYWRzaGVldHMvZC8xYXFPWFlzVTF1YmtieElaSV81cDhaUm9vdGdPQVQwS3dlTUEzTHZTWjdIWS9lZGl0I2dpZD0wKSBhbmQgc2F2ZWQgYXMgYSBUU1YgYXQgYGRhdGEvQ2FuY2VyIEltbXVub21vZHVsYXRvcnMgLSBUQ0dBIFBhbkltbXVuZSBHcm91cCAtIERpcmVjdCBSZWxhdGlvbnNoaXAudHN2YDoKCmBgYHtyfQpnZW5lX2NvcnJlbGF0ZV9maWxlIDwtICIuLi9kYXRhL0NhbmNlciBJbW11bm9tb2R1bGF0b3JzIC0gVENHQSBQYW5JbW11bmUgR3JvdXAgLSBEaXJlY3QgUmVsYXRpb25zaGlwLnRzdiIKZ2VuZV9jb3JyZWxhdGVfZGYgPC0gcmVhZF90c3YoZ2VuZV9jb3JyZWxhdGVfZmlsZSkKYGBgCgoKYGBge3J9Cm1ybmFfY29ycl9maWxlIDwtICIuLi9kYXRhL2ludGVybWVkaWF0ZS9tcm5hX2NvcnJlbGF0ZXNfZm9yX21pcm5hLmZlYXRoZXIiCmZvcmNlX2Zvcm1hdCA8LSBGQUxTRQppZiAoIWZpbGUuZXhpc3RzKG1ybmFfY29ycl9maWxlKSB8IGZvcmNlX2Zvcm1hdCkgewogICAgIyBsb2FkIG5vcm1hbGl6ZWQsIGJhdGNoLWNvcnJlY3RlZCBleHByZXNzaW9uIGRhdGEKICAgIG1ybmFfbm9ybV9maWxlIDwtIG1ybmFfZmlsZXMgJT4lIAogICAgICAgIGZpbHRlcihmaWxlLmlkID09ICJzeW40OTc2MzY5IikgJT4lIAogICAgICAgIC5bWyJmaWxlX3BhdGgiXV0KICAgIG1ybmFfbm9ybV9kZiA8LSByZWFkX3Rzdihtcm5hX25vcm1fZmlsZSwgcHJvZ3Jlc3MgPSBGQUxTRSkKICAgIAogICAgIyBnZW5lX2xpc3QgPC0gYygiSUZORyIsICJQUkYxIiwgIkdaTUEiLCAiUERDRDEiLCAiQ0QyNzQiLCAiUERDRDFMRzIiLCAiSUwxMCIsIAogICAgIyAgICAgICAgICAgICAgICAiVEdGQjEiLCAiSURPMSIsICJITEEtQSIpICAKICAgIG1ybmFfY29ycl9kZiA8LSBtcm5hX25vcm1fZGYgJT4lIAogICAgICAgIHNlcGFyYXRlKGdlbmVfaWQsIGMoImdlbmVfbmFtZSIsICJnZW5lX2lkIiksIHNlcCA9ICJcXHwiKSAlPiUgCiAgICAgICAgZmlsdGVyKChnZW5lX2lkICVpbiUgZ2VuZV9jb3JyZWxhdGVfZGYkYEVudHJleiBJRGApIAogICAgICAgICAgICAgICB8IChnZW5lX25hbWUgJWluJSBnZW5lX2NvcnJlbGF0ZV9kZiRgSEdOQyBTeW1ib2xgKSkgJT4lIAogICAgICAgIHNlbGVjdChvbmVfb2YoYygiZ2VuZV9uYW1lIiwgImdlbmVfaWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgbWlybmFfbXJuYV9zaGFyZWRfaWRzJFNhbXBsZUlEKSkpCiAgICB3cml0ZV9mZWF0aGVyKG1ybmFfY29ycl9kZiwgbXJuYV9jb3JyX2ZpbGUpCiAgICBybShtcm5hX25vcm1fZGYpCn0gZWxzZSB7CiAgICBtcm5hX2NvcnJfZGYgPC0gcmVhZF9mZWF0aGVyKG1ybmFfY29ycl9maWxlKQp9CmBgYAoKIyMjIERpc2Vhc2Utd2lzZSBjb3JyZWxhdGlvbnMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm1pcm5hX21ybmFfY29ycl9maWxlIDwtICIuLi9yZXN1bHRzL21pcm5hX21ybmFfY29ycmVsYXRpb24uZmVhdGhlciIKZm9yY2VfY29tcHV0ZSA8LSBGQUxTRQppZiAoIWZpbGUuZXhpc3RzKG1pcm5hX21ybmFfY29ycl9maWxlKSB8IGZvcmNlX2NvbXB1dGUpIHsKICAgIAogICAgZF9saXN0IDwtIG1pcm5hX3NhbXBsZV9wdF9kZiAlPiUKICAgICAgICBkaXN0aW5jdChEaXNlYXNlKSAlPiUgCiAgICAgICAgbXV0YXRlKERpc2Vhc2UgPSBhcy5jaGFyYWN0ZXIoRGlzZWFzZSkpICU+JSAKICAgICAgICAuJERpc2Vhc2UgJT4lCiAgICAgICAgc2V0X25hbWVzKC4pICU+JSAKICAgICAgICBhcy5saXN0KCkKICAgIAogICAgY29ycl9kZl9saXN0IDwtIG1jbGFwcGx5KGRfbGlzdCwgbWMuY29yZXMgPSA0LCBmdW5jdGlvbihkKSB7CiAgICAgICAgc2FtcGxlc19kIDwtIG1pcm5hX3NhbXBsZV9wdF9kZiAlPiUKICAgICAgICAgICAgZmlsdGVyKERpc2Vhc2UgPT0gZCkgJT4lCiAgICAgICAgICAgIHNlbGVjdCh2aWFsX2lkKSAlPiUKICAgICAgICAgICAgZmxhdHRlbl9jaHIoKSAlPiUKICAgICAgICAgICAgaW50ZXJzZWN0KHN0cl9yZXBsYWNlKG5hbWVzKG1ybmFfY29ycl9kZiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKFxcLVs6YWxudW06XSspezN9JCIsICIiKSkKCiAgICAgICAgc291cmNlX2RmIDwtIG1pcm5hX2NvcnJfZGYgJT4lCiAgICAgICAgICAgIHNldF9uYW1lcyhzdHJfcmVwbGFjZShuYW1lcyguKSwgIihcXC1bOmFsbnVtOl0rKXszfSQiLCAiIikpICU+JQogICAgICAgICAgICBzZWxlY3Qob25lX29mKGMoIkdlbmVzIiwgc2FtcGxlc19kKSkpICU+JQogICAgICAgICAgICBkcGx5cjo6cmVuYW1lKG1pcm5hID0gR2VuZXMpICU+JQogICAgICAgICAgICBnYXRoZXIoc2FtcGxlLCB4LCAtbWlybmEpICU+JSAKICAgICAgICAgICAgZmlsdGVyKCFpcy5uYSh4KSkKICAgICAgICAKICAgICAgICB0YXJnZXRfZGYgPC0gbXJuYV9jb3JyX2RmICU+JQogICAgICAgICAgICBzZXRfbmFtZXMoc3RyX3JlcGxhY2UobmFtZXMoLiksICIoXFwtWzphbG51bTpdKyl7M30kIiwgIiIpKSAlPiUKICAgICAgICAgICAgc2VsZWN0KG9uZV9vZihjKCJnZW5lX25hbWUiLCBzYW1wbGVzX2QpKSkgJT4lCiAgICAgICAgICAgIGRwbHlyOjpyZW5hbWUoY29ycmVsYXRlID0gZ2VuZV9uYW1lKSAlPiUKICAgICAgICAgICAgZ2F0aGVyKHNhbXBsZSwgeCwgLWNvcnJlbGF0ZSkgJT4lIAogICAgICAgICAgICBmaWx0ZXIoIWlzLm5hKHgpKQogICAgICAgIAogICAgICAgIGNvcnJfZGYgPC0gaW5uZXJfam9pbihzb3VyY2VfZGYsIHRhcmdldF9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAic2FtcGxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4ID0gYygiX3NvdXJjZSIsICJfdGFyZ2V0IikpICU+JQogICAgICAgICAgICBncm91cF9ieShtaXJuYSwgY29ycmVsYXRlKSAlPiUKICAgICAgICAgICAgZG8odGlkeShjb3IudGVzdCguJHhfc291cmNlLCAuJHhfdGFyZ2V0LCBtZXRob2QgPSAic3BlYXJtYW4iKSkpICU+JSAKICAgICAgICAgICAgdW5ncm91cCgpCiAgICAgICAgY29ycl9kZltbInAuYWRqdXN0Il1dIDwtIHAuYWRqdXN0KGNvcnJfZGYkcC52YWx1ZSwgbWV0aG9kID0gIkJIIikKICAgICAgICByZXR1cm4oY29ycl9kZikKICAgIH0pCiAgICAKICAgIG1pcm5hX21ybmFfY29ycl9kZiA8LSBiaW5kX3Jvd3MoY29ycl9kZl9saXN0LCAuaWQgPSAiZGlzZWFzZSIpICU+JSAKICAgICAgICBtdXRhdGUoY29ycmVsYXRlX3R5cGUgPSAibVJOQSBleHByZXNzaW9uIikKICAgIHdyaXRlX2ZlYXRoZXIobWlybmFfbXJuYV9jb3JyX2RmLCBtaXJuYV9tcm5hX2NvcnJfZmlsZSkKfSBlbHNlIHsKICAgIG1pcm5hX21ybmFfY29ycl9kZiA8LSByZWFkX2ZlYXRoZXIobWlybmFfbXJuYV9jb3JyX2ZpbGUpCn0KYGBgCgotLS0tLQoKXCAgCgojIFN1bW1hcml6ZSByZXN1bHRzCgojIyBBZGQgY29sb3IgcGFsZXR0ZQoKYGBge3J9CmdldF9jZXRfcGFsIDwtIGZ1bmN0aW9uKGNldF9wYXRoKSB7CiAgICByZWFkX2xpbmVzKGNldF9wYXRoKSAlPiUKICAgICAgICBhcy5saXN0KCkgJT4lCiAgICAgICAgbWFwX2NocihmdW5jdGlvbihjKSB7CiAgICAgICAgICAgIGNfcmdiIDwtIHN0cl9zcGxpdF9maXhlZChzdHJpbmcgPSBjLCBwYXR0ZXJuID0gIiwiLCBuID0gMykKICAgICAgICAgICAgcmdiKGNfcmdiWzFdLCBjX3JnYlsyXSwgY19yZ2JbM10sIG1heENvbG9yVmFsdWUgPSAyNTUpCiAgICAgICAgfSkKfQoKIyBoZXJlJ3MgYW4gZXhhbXBsZSB3aXRoIHRoZSBkaXZlcmdpbmcgYmx1ZS10byB5ZWxsb3cgY29sb3JtYXA6CmNldF9wYXRoIDwtICJ+L0Rvd25sb2Fkcy9DRVRwZXJjZXB0dWFsX2Nzdl8wXzI1NS9kaXZlcmdpbmctbGluZWFyX2JqeV8zMC05MF9jNDVfbjI1Ni5jc3YiCm15X2NldF9wYWwgPC0gZ2V0X2NldF9wYWwoY2V0X3BhdGgpCmBgYAoKCiMjIEV4dGVybmFsIG1pUk5BIGRhdGEKCiMjIyBSZWd1bGF0b3IgbWlScwoKYGBge3J9Cm1pcm5hX2NhdXNhbF9kZiA8LSByZWFkeGw6OnJlYWRfZXhjZWwoIi4uL2RhdGEvY2F1c2FsX1RDR0FfcGFuQ2FuY2VyX21pUk5BX2ltbXVuZUluZmlsdHJhdGVfM18xNV8yMDE3Lnhsc3giKSAlPiUgCiAgICAuWywgMTo5XSAlPiUgCiAgICBtdXRhdGUobWlybmFfZGlzZWFzZSA9IHN0cl9jKGBtaVJOQSBOYW1lYCwgYFR1bW9yIFR5cGVgLCBzZXAgPSAiXyIpKQpgYGAKCiMjIyBLbm93biBpbW11bmUgbWlScwoKYGBge3J9Cm1pcm5hX2ltbXVuZV9kZiA8LSByZWFkeGw6OnJlYWRfZXhjZWwoIi4uL2RhdGEvUGFsYWRpbmlfaW1tdW5lX21pUk5Bcy54bHN4IikKYGBgCgpgYGB7cn0KbWlybmFfaW1tdW5lX3RpZHlfZGYgPC0gbWlybmFfaW1tdW5lX2RmICU+JQogICAgbXV0YXRlKG1pcm5hX2dyb3VwID0gc3RyX3NwbGl0KE1pY3JvUk5BcywgIiwiKSkgJT4lCiAgICB1bm5lc3QobWlybmFfZ3JvdXApICU+JQogICAgbXV0YXRlKG1pcm5hX2dyb3VwID0gc3RyX3RyaW0obWlybmFfZ3JvdXAsICJib3RoIikpICU+JQogICAgIyBkaXN0aW5jdChtaXJuYV9ncm91cCkgJT4lCiAgICBtdXRhdGUobWlybmEgPSBtaXJuYV9ncm91cCkgJT4lCiAgICAjIGZpbHRlcihDYXRlZ29yeSAlaW4lIGMoIklubmF0ZSBpbW11bml0eSIsICJBZGFwdGl2ZSBpbW11bml0eSIpKSAlPiUKICAgICMgZmlsdGVyKHN0cl9kZXRlY3QobWlybmEsICJjbHVzdGVyIikpICU+JQogICAgZmlsdGVyKCFzdHJfZGV0ZWN0KG1pcm5hX2dyb3VwLCAiWzAtOV0rW2Etel17Mix9IikpICU+JSAKICAgIG11dGF0ZShtaXJuYSA9IHN0cl9yZXBsYWNlKG1pcm5hLCAibWlSLTE3LzkyIGNsdXN0ZXIiLCAibWlSLTE3LG1pUi0xOGEsbWlSLTE5YSxtaVItMjBhLG1pUi0xOWItMSxtaVItOTJhLTEiKSwKICAgICAgICAgICBtaXJuYSA9IHN0cl9yZXBsYWNlKG1pcm5hLCAibWlSLTIxMi8xMzIgY2x1c3RlciIsICJtaVItMjEyLG1pUi0xMzIiKSwKICAgICAgICAgICBtaXJuYSA9IHN0cl9yZXBsYWNlKG1pcm5hLCAibWlSLTEwIGZhbWlseSIsICJtaVItMTBhLG1pUi0xMGIsbWlSLTk5YSxtaVItOTliLG1pUi0xMDAsbWlSLTEyNWEsbWlSLTEyNWItMSxtaVItMTI1Yi0yIiksCiAgICAgICAgICAgbWlybmEgPSBzdHJfcmVwbGFjZShtaXJuYSwgIm1pUi0yMjEvMjIyIiwgIm1pUi0yMjEsbWlSLTIyMiIpLAogICAgICAgICAgIG1pcm5hID0gc3RyX3JlcGxhY2UobWlybmEsICJtaVItMTBhL2IiLCAibWlSLTEwYSxtaVItMTBiIiksCiAgICAgICAgICAgbWlybmEgPSBzdHJfcmVwbGFjZShtaXJuYSwgIm1pUi0xNDgvMTUyIiwgIm1pUi0xNDgsbWlSLTE1MiIpLAogICAgICAgICAgIG1pcm5hID0gc3RyX3JlcGxhY2UobWlybmEsICJtaVItMTctNXAvMjBhIiwgIm1pUi0xNy01cCxtaVItMjBhIiksCiAgICAgICAgICAgbWlybmEgPSBzdHJfcmVwbGFjZShtaXJuYSwgIm1pUi0yMjEvMjIyIGNsdXN0ZXIiLCAibWlSLTIyMSxtaVItMjIyIiksCiAgICAgICAgICAgbWlybmEgPSBzdHJfcmVwbGFjZShtaXJuYSwgIm1pUi0xODFhL2IiLCAibWlSLTE4MWEsbWlSLTE4MWIiKSwKICAgICAgICAgICBtaXJuYSA9IHN0cl9yZXBsYWNlKG1pcm5hLCAibWlSLTE1LzE2IiwgIm1pUi0xNSxtaVItMTYiKSwKICAgICAgICAgICBtaXJuYSA9IHN0cl9yZXBsYWNlKG1pcm5hLCAibWlSLTE4MSBmYW1pbHkiLCAibWlSLTE4MWEtMSxtaVItMTgxYS0yLG1pUi0xODFiLTEsbWlSLTE4MWItMixtaVItMTgxYyxtaVItMTgxZCIpLAogICAgICAgICAgIG1pcm5hID0gc3RyX3JlcGxhY2UobWlybmEsICJtaVItMTMwLzMwMSIsICJtaVItMTMwLG1pUi0zMDEiKSwKICAgICAgICAgICBtaXJuYSA9IHN0cl9yZXBsYWNlKG1pcm5hLCAibWlSLTk5YS9taVItMTUwIiwgIm1pUi05OWEsbWlSLTE1MCIpLAogICAgICAgICAgIG1pcm5hID0gc3RyX3JlcGxhY2UobWlybmEsICJMZXQiLCAibGV0IikpICU+JQogICAgbXV0YXRlKG1pcm5hID0gc3RyX3NwbGl0KG1pcm5hLCAiLCIpKSAlPiUgCiAgICB1bm5lc3QobWlybmEpICU+JSAKICAgICMgbGVmdF9qb2luKG1pcm5hX3NpZ19jb3JyX2RmICU+JQogICAgIyAgICAgICAgICAgICAgIGZpbHRlcihjb3JyZWxhdGVfdHlwZSA9PSAiQ0lCRVJTT1JUIGZyYWN0aW9uIikgJT4lCiAgICAjICAgICAgICAgICAgICAgbXV0YXRlKG1pcm5hX3Nob3J0ID0gc3RyX2V4dHJhY3QoCiAgICAjICAgICAgICAgICAgICAgICAgIG1pcm5hLCAiKD88PWhzYVxcLSlbOmFsbnVtOl0rXFwtWzphbG51bTpdKyIpCiAgICAjICAgICAgICAgICAgICAgICAgICksCiAgICAjICAgICAgICAgICBieSA9IGMoIm1pcm5hIiA9ICJtaXJuYV9zaG9ydCIpKSAlPiUKICAgICMgZmlsdGVyKCFpcy5uYShkaXNlYXNlKSkgJT4lCiAgICAjIGdyb3VwX2J5KENlbGxfbGluZWFnZSwgQ2VsbHVsYXJfcHJvY2VzcywgbWlybmEsIGdyb3VwLCBsYWJlbCkgJT4lCiAgICAjIHRhbGx5KCkgJT4lCiAgICAjIHVuZ3JvdXAoKSAlPiUKICAgICMgZmlsdGVyKGdyb3VwID09ICJBZGFwdGl2ZSBJbW11bmUgQ2VsbHMiLAogICAgIyAgICAgICAgc3RyX2RldGVjdChDZWxsX2xpbmVhZ2UsICJeVCIpLAogICAgIyAgICAgICAgc3RyX2RldGVjdChsYWJlbCwgIl5UIikpICU+JQogICAgIyBkaXN0aW5jdChDZWxsX2xpbmVhZ2UsIGxhYmVsKSAlPiUKICAgICMgbXV0YXRlKENlbGxfbGluZWFnZSA9IHN0cl9yZXBsYWNlKENlbGxfbGluZWFnZSwgIlQgIiwgIlQgY2VsbHMgIiksCiAgICAjICAgICAgICBDZWxsX2xpbmVhZ2UgPSBzdHJfcmVwbGFjZShDZWxsX2xpbmVhZ2UsICIgY2VsbHMkIiwgIiIpLAogICAgIyAgICAgICAgbGFiZWwgPSBzdHJfcmVwbGFjZV9hbGwobGFiZWwsICJcXC4iLCAiICIpLAogICAgIyAgICAgICAgbGFiZWwgPSBzdHJfcmVwbGFjZShsYWJlbCwgIkNENCIsICJoZWxwZXIiKSwKICAgICMgICAgICAgIGxhYmVsID0gc3RyX3JlcGxhY2UobGFiZWwsICJDRDgiLCAiY3l0b3RveGljIikpICU+JQogICAgIyBmaWx0ZXIoIShsYWJlbCAlaW4lIENlbGxfbGluZWFnZSkgJiAhKENlbGxfbGluZWFnZSAlaW4lIGxhYmVsKSkgJT4lCiAgICAjIG11dGF0ZShsaW5lYWdlX21hdGNoID0gc3RyX2RldGVjdChsYWJlbCwgQ2VsbF9saW5lYWdlKSkgJT4lCiAgICAjIGdyb3VwX2J5KENlbGxfbGluZWFnZSkgJT4lCiAgICAjIHN1bW1hcml6ZShudW1fbWF0Y2hlcyA9IHN1bShsaW5lYWdlX21hdGNoKSkgJT4lCiAgICBJCmBgYAoKIyMjIFByZWRpY3RlZCBiaW5kaW5nIHRhcmdldHMKCmBgYHtyfQptaXJuYV90YXJnZXRfZGYgPC0gcmVhZF90c3YoIi4uL2RhdGEvbWlSREJfdjUuMF9wcmVkaWN0aW9uX3Jlc3VsdC50eHQiLCBjb2xfbmFtZXMgPSBGQUxTRSkgJT4lIAogICAgc2V0X25hbWVzKGMoIm1pcm5hIiwgImdlbmUiLCAic2NvcmUiKSkgJT4lIAogICAgZmlsdGVyKHN0cl9kZXRlY3QobWlybmEsICJoc2EiKSkKYGBgCgpgYGB7cn0KbWlybmFfbXJuYV90YXJnZXRfZGYgPC0gcmVhZF90c3YoIi4uL2RhdGEvc3luZXJnaXplci50c3YiLCBza2lwID0gNCkgJT4lIAogICAgbXV0YXRlKHJlZnNlcV9tcm5hID0gc3RyX3NwbGl0KHJlZnNlcV9tcm5hLCAiICIpKSAlPiUgCiAgICB1bm5lc3QocmVmc2VxX21ybmEpICU+JSAKICAgIGZpbHRlcihyZWZzZXFfbXJuYSAlaW4lIG1pcm5hX3RhcmdldF9kZiRnZW5lKSAlPiUgCiAgICBsZWZ0X2pvaW4obWlybmFfdGFyZ2V0X2RmLCBieSA9IGMoInJlZnNlcV9tcm5hIiA9ICJnZW5lIikpICU+JSAKICAgIG11dGF0ZShtaXJuYV90YXJnZXQgPSBzdHJfYyhtaXJuYSwgaGduY19zeW1ib2wsIHNlcCA9ICJfIikpCmBgYAoKXCAgCgojIyBEaXN0cmlidXRpb24gb2Ygc2lnbmlmaWNhbnQgY29ycmVsYXRpb25zIGFjcm9zcyB0dW1vciB0eXBlcwoKIyMjIEFnZ3JlZ2F0ZSBjb3VudHMgYnkgY29ycmVsYXRlIHR5cGUKCmBgYHtyfQpiaW5kX3Jvd3MobWlybmFfbGV1a19mcmFjX2NvcnJfZGYsIG1pcm5hX2NpYmVyX2ZyYWNfY29ycl9kZiwgCiAgICAgICAgICBtaXJuYV9tdXRfbG9hZF9jb3JyX2RmLCBtaXJuYV90Y3JfZGl2X2NvcnJfZGYsCiAgICAgICAgICBtaXJuYV9tcm5hX2NvcnJfZGYpICU+JSAKICAgIGdyb3VwX2J5KGRpc2Vhc2UsIGNvcnJlbGF0ZV90eXBlKSAlPiUgCiAgICBzdW1tYXJpc2UobnVtX2NvcnJlbGF0aW9ucyA9IGxlbmd0aChlc3RpbWF0ZSksCiAgICAgICAgICAgICAgc2lnbmlmaWNhbnQgPSBzdW0ocC5hZGp1c3QgPCAwLjA1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgIG90aGVyID0gbnVtX2NvcnJlbGF0aW9ucyAtIHNpZ25pZmljYW50KSAlPiUgCiAgICBnYXRoZXIoY29ycmVsYXRpb25zLCB0b3RhbCwgLWRpc2Vhc2UsIC1jb3JyZWxhdGVfdHlwZSwgLW51bV9jb3JyZWxhdGlvbnMpICU+JSAKICAgIGdncGxvdChhZXMoeCA9IGRpc2Vhc2UsIHkgPSB0b3RhbCkpICsKICAgIGdlb21fY29sKGFlcyhmaWxsID0gZGlzZWFzZSwgYWxwaGEgPSBjb3JyZWxhdGlvbnMpLCBjb2xvdXIgPSAic2xhdGVncmF5IikgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUpLAogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHRjZ2FfY29sb3JzJENvbG9yKSArCiAgICBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzID0gYygwLjQsIDEpKSArCiAgICBndWlkZXMoZmlsbCA9IEZBTFNFKSArCiAgICBmYWNldF93cmFwKH4gY29ycmVsYXRlX3R5cGUsIG5jb2wgPSAyLCBzY2FsZXMgPSAiZnJlZV95IikKYGBgCgojIyMgRGVmaW5pbmcgbW9yZSBzcGVjaWZpYyBncm91cHMgd2l0aGluIGNvcnJlbGF0ZSB0eXBlcwoKYGBge3J9CmNvcnJlbGF0ZV9hbm5vdGF0aW9ucyA8LSBtaXJuYV9tcm5hX2NvcnJfZGYgJT4lIAogICAgc2VsZWN0KGRpc2Vhc2UsIG1pcm5hLCBjb3JyZWxhdGUsIGVzdGltYXRlLCBzdGF0aXN0aWMsIHAudmFsdWUsIG1ldGhvZCwgYWx0ZXJuYXRpdmUsIHAuYWRqdXN0LCBjb3JyZWxhdGVfdHlwZSkgJT4lIAogICAgYmluZF9yb3dzKG1pcm5hX2xldWtfZnJhY19jb3JyX2RmLCBtaXJuYV9jaWJlcl9mcmFjX2NvcnJfZGYsIAogICAgICAgICAgbWlybmFfbXV0X2xvYWRfY29ycl9kZiwgLikgJT4lIAogICAgZGlzdGluY3QoY29ycmVsYXRlLCBjb3JyZWxhdGVfdHlwZSkgJT4lIAogICAgbGVmdF9qb2luKGdlbmVfY29ycmVsYXRlX2RmLAogICAgICAgICAgICAgIGJ5ID0gYygiY29ycmVsYXRlIiA9ICJtYXRjaGVkX25hbWUiKSkgJT4lICAKICAgIG11dGF0ZShsYWJlbCA9IGlmZWxzZShjb3JyZWxhdGVfdHlwZSA9PSAibVJOQSBleHByZXNzaW9uIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgYEhHTkMgU3ltYm9sYCwgY29ycmVsYXRlKSwKICAgICAgICAgICBsYWJlbCA9IHN0cl9yZXBsYWNlKGNvcnJlbGF0ZSwgIlxcLlxcLlRyZWdzXFwuIiwgIiIpLAogICAgICAgICAgIGxhYmVsID0gaWZlbHNlKGxhYmVsID09ICJsZXVrX2ZyYWMiLCAiTGV1a29jeXRlIEZyYWN0aW9uIiwgbGFiZWwpLAogICAgICAgICAgIGdyb3VwID0gaWZlbHNlKGNvcnJlbGF0ZV90eXBlID09ICJsZXVrb2N5dGUgZnJhY3Rpb24iLCAiVG90YWwgSW1tdW5lIENlbGxzIiwgZ3JvdXApLAogICAgICAgICAgIGdyb3VwID0gaWZlbHNlKGNvcnJlbGF0ZV90eXBlID09ICJtdXRhdGlvbiBsb2FkIiwgIk11dGF0aW9uIExvYWQiLCBncm91cCksCiAgICAgICAgICAgZ3JvdXAgPSBpZmVsc2UoY29ycmVsYXRlX3R5cGUgPT0gIkNJQkVSU09SVCBmcmFjdGlvbiIgJiBzdHJfZGV0ZWN0KGxhYmVsLCAiXihUfEIpXFwuIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICJBZGFwdGl2ZSBJbW11bmUgQ2VsbHMiLCBncm91cCksCiAgICAgICAgICAgZ3JvdXAgPSBpZmVsc2UoY29ycmVsYXRlX3R5cGUgPT0gIkNJQkVSU09SVCBmcmFjdGlvbiIgJiAhc3RyX2RldGVjdChsYWJlbCwgIl4oVHxCKSIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiSW5uYXRlIEltbXVuZSBDZWxscyIsIGdyb3VwKSkgJT4lIAogICAgc2VsZWN0KGNvcnJlbGF0ZSwgY29ycmVsYXRlX3R5cGUsIGdyb3VwLCBsYWJlbCkKYGBgCgpgYGB7cn0KbWlybmFfc2lnX2NvcnJfZGYgPC0gYmluZF9yb3dzKG1pcm5hX2xldWtfZnJhY19jb3JyX2RmLCBtaXJuYV9jaWJlcl9mcmFjX2NvcnJfZGYsIAogICAgICAgICAgbWlybmFfbXV0X2xvYWRfY29ycl9kZiwgbWlybmFfdGNyX2Rpdl9jb3JyX2RmLAogICAgICAgICAgbWlybmFfbXJuYV9jb3JyX2RmKSAlPiUgCiAgICBmaWx0ZXIoIShkaXNlYXNlICVpbiUgYygiRExCQyIsICJUSFlNIiwgIkxBTUwiKSkpICU+JSAKICAgIGZpbHRlcighaXMubmEocC5hZGp1c3QpLCAKICAgICAgICAgICBwLmFkanVzdCA8IDAuMDUpICU+JSAKICAgIG11dGF0ZShkaXNlYXNlID0gZmFjdG9yKGRpc2Vhc2UsIGxldmVscyA9IHRjZ2FfY29sb3JzJERpc2Vhc2UpKSAlPiUgCiAgICBsZWZ0X2pvaW4oY29ycmVsYXRlX2Fubm90YXRpb25zLCBieSA9IGMoImNvcnJlbGF0ZSIsICJjb3JyZWxhdGVfdHlwZSIpKSAlPiUKICAgIGZpbHRlcighaXMubmEoZ3JvdXApKQpgYGAKCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NX0KbWlybmFfc2lnX2NvcnJfZGYgJT4lIAogICAgbXV0YXRlKGRpcmVjdGlvbiA9IGlmZWxzZShlc3RpbWF0ZSA+IDAsICJwb3NpdGl2ZSIsICJuZWdhdGl2ZSIpKSAlPiUKICAgIGdyb3VwX2J5KGRpc2Vhc2UsIGNvcnJlbGF0ZV90eXBlLCBkaXJlY3Rpb24pICU+JSAKICAgIHRhbGx5KCkgJT4lIAogICAgdW5ncm91cCgpICU+JSAKICAgIG11dGF0ZShjb3VudCA9IGlmZWxzZShkaXJlY3Rpb24gPT0gIm5lZ2F0aXZlIiwgLTEgKiBuLCBuKSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gZGlzZWFzZSwgeSA9IGNvdW50KSkgKwogICAgZ2VvbV9jb2woYWVzKGZpbGwgPSBkaXNlYXNlLCBhbHBoYSA9IGRpcmVjdGlvbiksIGNvbG91ciA9ICJzbGF0ZWdyYXkiKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHRjZ2FfY29sb3JzJENvbG9yKSArCiAgICBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzID0gYygwLjQsIDEpKSArCiAgICBndWlkZXMoZmlsbCA9IEZBTFNFKSArCiAgICBmYWNldF93cmFwKH4gY29ycmVsYXRlX3R5cGUsIG5jb2wgPSAyLCBzY2FsZXMgPSAiZnJlZV95IikKYGBgCgpgYGB7cn0KbWlybmFfc3VwcG9ydF9kZiA8LSBtaXJuYV9zaWdfY29ycl9kZiAlPiUKICAgIHNlbGVjdChkaXNlYXNlLCBtaXJuYSwgY29ycmVsYXRlLCBlc3RpbWF0ZSwgcC5hZGp1c3QsIGNvcnJlbGF0ZV90eXBlLAogICAgICAgICAgIGdyb3VwLCBsYWJlbCkgJT4lCiAgICBtdXRhdGUobWlybmFfdGFyZ2V0ID0gc3RyX2MobWlybmEsIGNvcnJlbGF0ZSwgc2VwID0gIl8iKSwKICAgICAgICAgICBtaXJuYV9kaXNlYXNlID0gc3RyX2MobWlybmEsIGRpc2Vhc2UsIHNlcCA9ICJfIiksCiAgICAgICAgICAgbWlybmFfc2hvcnQgPSBzdHJfZXh0cmFjdChtaXJuYSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKD88PWhzYVxcLSkuKiIpLAogICAgICAgICAgIG1pcm5hX3Nob3J0X2FtYmlndW91cyA9IHN0cl9leHRyYWN0KG1pcm5hX3Nob3J0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJbOmFsbnVtOl0rXFwtWzphbG51bTpdKyIpKSAlPiUgCiAgICBtdXRhdGUoc2lnbmlmaWNhbnQgPSBUUlVFLAogICAgICAgICAgIGltbXVub21vZHVsYXRvciA9IGNvcnJlbGF0ZV90eXBlID09ICJtUk5BIGV4cHJlc3Npb24iLAogICAgICAgICAgIGluZmlsdHJhdGUgPSBjb3JyZWxhdGVfdHlwZSAlaW4lIGMoIkNJQkVSU09SVCBmcmFjdGlvbiIsICJsZXVrb2N5dGUgZnJhY3Rpb24iKSwKICAgICAgICAgICBsZXVrb2N5dGUgPSBjb3JyZWxhdGVfdHlwZSA9PSAibGV1a29jeXRlIGZyYWN0aW9uIiwKICAgICAgICAgICBjZWxsdHlwZSA9IGNvcnJlbGF0ZV90eXBlID09ICJDSUJFUlNPUlQgZnJhY3Rpb24iLAogICAgICAgICAgIG1pcmJhc2Vfc3VwcG9ydCA9IChlc3RpbWF0ZSA8IDApICYgKGNvcnJlbGF0ZV90eXBlID09ICJtUk5BIGV4cHJlc3Npb24iKSAmCiAgICAgICAgICAgICAgIG1pcm5hX3RhcmdldCAlaW4lIG1pcm5hX21ybmFfdGFyZ2V0X2RmJG1pcm5hX3RhcmdldCwKICAgICAgICAgICBzeWduYWxfc3VwcG9ydCA9IChjb3JyZWxhdGVfdHlwZSAlaW4lIGMoIkNJQkVSU09SVCBmcmFjdGlvbiIsICJsZXVrb2N5dGUgZnJhY3Rpb24iKSkgJgogICAgICAgICAgICAgICBtaXJuYV9kaXNlYXNlICVpbiUgbWlybmFfY2F1c2FsX2RmJG1pcm5hX2Rpc2Vhc2UsCiAgICAgICAgICAgaW1tdW5lX3N1cHBvcnQgPSBtaXJuYV9zaG9ydCAlaW4lIG1pcm5hX2ltbXVuZV90aWR5X2RmJG1pcm5hIHwKICAgICAgICAgICAgICAgbWlybmFfc2hvcnRfYW1iaWd1b3VzICVpbiUgbWlybmFfaW1tdW5lX3RpZHlfZGYkbWlybmEsCiAgICAgICAgICAgc3Ryb25nID0gYWJzKGVzdGltYXRlKSA+IDAuNSwKICAgICAgICAgICBpbW11bmVfc3Ryb25nID0gaW1tdW5lX3N1cHBvcnQgJiBzdHJvbmcpCmBgYAoKYGBge3J9Cm1pcm5hX3N1cHBvcnRfZGYgJT4lIAogICAgZ2F0aGVyKGZsYWcsIHN0YXR1cywgc2lnbmlmaWNhbnQsIHN0cm9uZywgaW1tdW5lX3N0cm9uZywKICAgICAgICAgICBpbW11bm9tb2R1bGF0b3IsIGluZmlsdHJhdGUsIGxldWtvY3l0ZSwgY2VsbHR5cGUsCiAgICAgICAgICAgbWlyYmFzZV9zdXBwb3J0LCBzeWduYWxfc3VwcG9ydCwgaW1tdW5lX3N1cHBvcnQpICU+JSAKICAgIGdyb3VwX2J5KG1pcm5hLCBmbGFnKSAlPiUKICAgIHN1bW1hcml6ZShzdXBwb3J0ID0gbl9kaXN0aW5jdChkaXNlYXNlW3N0YXR1c10pKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUgCiAgICBmaWx0ZXIoc3VwcG9ydCA+IDApICU+JSAKICAgIGdyb3VwX2J5KGZsYWcpICU+JSAKICAgIHRhbGx5KCkKYGBgCgpgYGB7cn0KbWlybmFfc3VwcG9ydF9kZiAlPiUgCiAgICBnYXRoZXIoZmxhZywgc3RhdHVzLCBzaWduaWZpY2FudCwgc3Ryb25nLAogICAgICAgICAgIGltbXVub21vZHVsYXRvciwgaW5maWx0cmF0ZSwgbGV1a29jeXRlLCBjZWxsdHlwZSwKICAgICAgICAgICBtaXJiYXNlX3N1cHBvcnQsIHN5Z25hbF9zdXBwb3J0LCBpbW11bmVfc3VwcG9ydCkgJT4lIAogICAgZ3JvdXBfYnkoZGlzZWFzZSwgZmxhZykgJT4lCiAgICBzdW1tYXJpemUoc3VwcG9ydCA9IHN1bShzdGF0dXMpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUgCiAgICBzcHJlYWQoZmxhZywgc3VwcG9ydCkgJT4lIAogICAgc2VsZWN0KG9uZV9vZihjKCJkaXNlYXNlIiwgInNpZ25pZmljYW50IiwgInN0cm9uZyIsICJpbW11bmVfc3VwcG9ydCIsIAogICAgICAgICAgICAgImltbXVub21vZHVsYXRvciIsICJtaXJiYXNlX3N1cHBvcnQiLAogICAgICAgICAgICAgImluZmlsdHJhdGUiLCAibGV1a29jeXRlIiwgImNlbGx0eXBlIiwgInN5Z25hbF9zdXBwb3J0IikpKQpgYGAKCgojIyMgR3JvdXBzIGFuZCBjb3JyZWxhdGVzIHdpdGggZ3JlYXRlc3QgbnVtYmVycyBvZiBjb3JyZWxhdGVkIG1pUk5BcwoKYGBge3J9Cm1pcm5hX3NpZ19jb3JyX2RmICU+JSAKICAgIGZpbHRlcighaXMubmEoZ3JvdXApKSAlPiUgCiAgICBncm91cF9ieShjb3JyZWxhdGVfdHlwZSwgZ3JvdXAsIGxhYmVsLCBkaXNlYXNlKSAlPiUgCiAgICBzdW1tYXJpc2Uobl9taXJuYSA9IG5fZGlzdGluY3QobWlybmEpKSAlPiUgCiAgICB1bmdyb3VwKCkgJT4lIAogICAgZ3JvdXBfYnkoY29ycmVsYXRlX3R5cGUsIGdyb3VwLCBsYWJlbCkgJT4lIAogICAgbXV0YXRlKG1lYW5fbWlybmEgPSBtZWFuKG5fbWlybmEpKSAlPiUgCiAgICB1bmdyb3VwKCkgJT4lIAogICAgYXJyYW5nZShjb3JyZWxhdGVfdHlwZSwgZ3JvdXAsIGRlc2MobWVhbl9taXJuYSkpICU+JSAKICAgIG11dGF0ZShncm91cCA9IGZjdF9pbm9yZGVyKGdyb3VwKSwKICAgICAgICAgICBsYWJlbCA9IGZjdF9pbm9yZGVyKGxhYmVsKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBsYWJlbCwgeSA9IG5fbWlybmEpKSArCiAgICBnZW9tX2JveHBsb3QoYWVzKGZpbGwgPSBncm91cCksIAogICAgICAgICAgICAgICAgIG91dGxpZXIuc2l6ZSA9IDAsIG91dGxpZXIuYWxwaGEgPSAwKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9maWxsX2Rpc2NyZXRlKCkgKwogICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQodGl0bGUgPSBOVUxMKSkgKwogICAgeWxhYigiTnVtLiBjb3JyZWxhdGVkIG1pUk5BcyIpICsKICAgIHhsYWIoIiIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgojIyBGcmVxdWVudGx5IGFzc29jaWF0ZWQgbWlSTkFzCgojIyMgV2l0aCBsZXVrb2N5dGUgZnJhY3Rpb24ocykgJiBjYXVzYWwgcHJlZGljdGlvbgoKYGBge3J9Cm1pcm5hX2xldWtfY29tbW9uX2RmIDwtIG1pcm5hX3N1cHBvcnRfZGYgJT4lCiAgICBmaWx0ZXIoc3lnbmFsX3N1cHBvcnQgJiBpbW11bmVfc3VwcG9ydCkgJT4lIAogICAgZ3JvdXBfYnkobWlybmEsIGRpc2Vhc2UpICU+JSAKICAgIHN1bW1hcml6ZShudW1fdHlwZXMgPSBuX2Rpc3RpbmN0KGdyb3VwKSkgJT4lIAogICAgdW5ncm91cCgpICU+JSAKICAgIGdyb3VwX2J5KG1pcm5hKSAlPiUKICAgIG11dGF0ZShuel9kaXNlYXNlID0gbl9kaXN0aW5jdChkaXNlYXNlW251bV90eXBlcyA+IDBdKSkgJT4lCiAgICB1bmdyb3VwKCkgJT4lCiAgICBmaWx0ZXIobnpfZGlzZWFzZSA+IDIpCmBgYAoKYGBge3J9Cm1pcm5hX2xldWtfY29tbW9uX2RmICU+JSAKICAgIGdncGxvdChhZXMoeCA9IGRpc2Vhc2UsIHkgPSBtaXJuYSkpICsKICAgIGdlb21fdGlsZShhZXMoZmlsbCA9IG51bV90eXBlcykpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpCmBgYAoKYGBge3IsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTMuNX0KcGxvdF9jb2xvcnMgPC0gdGNnYV9jb2xvcnMgJT4lIAogICAgZmlsdGVyKERpc2Vhc2UgJWluJSBtaXJuYV9sZXVrX2NvbW1vbl9kZiRkaXNlYXNlKQoKcCA8LSBtaXJuYV9zdXBwb3J0X2RmICU+JSAKICAgIGZpbHRlcihzeWduYWxfc3VwcG9ydCAmIGltbXVuZV9zdXBwb3J0LAogICAgICAgICAgIG1pcm5hICVpbiUgbWlybmFfbGV1a19jb21tb25fZGYkbWlybmEpICU+JSAKICAgIG11dGF0ZShncm91cCA9IGZjdF9pbm9yZGVyKGdyb3VwKSwKICAgICAgICAgICBtaXJuYSA9IHN0cl9leHRyYWN0KG1pcm5hLCAibWlSLioiKSwKICAgICAgICAgICBjb3JyZWxhdGlvbiA9IGlmZWxzZShlc3RpbWF0ZSA+IDAsICJwb3NpdGl2ZSIsICJuZWdhdGl2ZSIpLAogICAgICAgICAgIGRpc2Vhc2UgPSBmYWN0b3IoZGlzZWFzZSwgbGV2ZWxzID0gcGxvdF9jb2xvcnMkRGlzZWFzZSkpICU+JQogICAgZ2dwbG90KGFlcyh5ID0gbGFiZWwsIHggPSBkaXNlYXNlKSkgKwogICAgZ2VvbV90aWxlKGFlcyhmaWxsID0gZGlzZWFzZSwgY29sb3VyID0gY29ycmVsYXRpb24pLCBzaXplID0gMC40KSArCiAgICBzY2FsZV9maWxsX21hbnVhbCgiVHVtb3IgR3JvdXAiLCB2YWx1ZXMgPSBwbG90X2NvbG9ycyRDb2xvcikgKwogICAgc2NhbGVfY29sb3VyX21hbnVhbCgiQ29ycmVsYXRpb24iLCB2YWx1ZXMgPSBjKG15X2NldF9wYWxbMV0sIG15X2NldF9wYWxbMjU2XSkpICsKICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG5yb3cgPSAzLCBieXJvdyA9IFRSVUUpLAogICAgICAgICAgIGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGZpbGwgPSAiI0NDQ0NDQyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXZlcnNlID0gVFJVRSkpICsKICAgIHlsYWIoIiIpICsKICAgIHhsYWIoIiIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgYW5nbGUgPSA5MCwgaGp1c3QgPSAwKSwKICAgICAgICAgIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMCksCiAgICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArCiAgICBmYWNldF9ncmlkKGdyb3VwIH4gbWlybmEsIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIpCnAKCmdnc2F2ZSgiLi4vZmlndXJlcy9taXJuYV9sZXVrX3N1cHBvcnRlZF9jb3JyLnBuZyIsIHAsCiAgICAgICB3aWR0aCA9IDE3LCBoZWlnaHQgPSA4LCB1bml0cyA9ICJjbSIsIGRwaSA9IDMwMCwgc2NhbGUgPSAyKQpgYGAKCiMjIyBXaXRoIGltbXVub21vZHVsYXRvcnMgJiBwcmVkaWN0ZWQgdGFyZ2V0cwoKYGBge3J9Cm1pcm5hX2ltbXVub21vZF9jb21tb25fZGYgPC0gbWlybmFfc3VwcG9ydF9kZiAlPiUKICAgIGZpbHRlcihtaXJiYXNlX3N1cHBvcnQgJiBpbW11bmVfc3VwcG9ydCkgJT4lIAogICAgZ3JvdXBfYnkobWlybmEsIGRpc2Vhc2UpICU+JSAKICAgIHN1bW1hcml6ZShudW1fdHlwZXMgPSBuX2Rpc3RpbmN0KGdyb3VwKSkgJT4lIAogICAgdW5ncm91cCgpICU+JSAKICAgIGdyb3VwX2J5KG1pcm5hKSAlPiUKICAgIG11dGF0ZShuel9kaXNlYXNlID0gbl9kaXN0aW5jdChkaXNlYXNlW251bV90eXBlcyA+IDBdKSkgJT4lCiAgICB1bmdyb3VwKCkgJT4lCiAgICBkaXN0aW5jdChtaXJuYSkKICAgIGZpbHRlcihuel9kaXNlYXNlID4gMTQpCmBgYAoKYGBge3J9Cm1pcm5hX2ltbXVub21vZF9jb21tb25fZGYgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gZGlzZWFzZSwgeSA9IG1pcm5hKSkgKwogICAgZ2VvbV90aWxlKGFlcyhmaWxsID0gbnVtX3R5cGVzKSkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9My41fQpwbG90X2NvbG9ycyA8LSB0Y2dhX2NvbG9ycyAlPiUgCiAgICBmaWx0ZXIoRGlzZWFzZSAlaW4lIG1pcm5hX2ltbXVub21vZF9jb21tb25fZGYkZGlzZWFzZSkKCnAgPC0gbWlybmFfc3VwcG9ydF9kZiAlPiUgCiAgICBmaWx0ZXIobWlyYmFzZV9zdXBwb3J0ICYgaW1tdW5lX3N1cHBvcnQsCiAgICAgICAgICAgbWlybmEgJWluJSBtaXJuYV9pbW11bm9tb2RfY29tbW9uX2RmJG1pcm5hKSAlPiUgCiAgICBtdXRhdGUoZ3JvdXAgPSBmY3RfaW5vcmRlcihncm91cCksCiAgICAgICAgICAgbWlybmEgPSBzdHJfZXh0cmFjdChtaXJuYSwgIm1pUi4qIiksCiAgICAgICAgICAgY29ycmVsYXRpb24gPSBpZmVsc2UoZXN0aW1hdGUgPiAwLCAicG9zaXRpdmUiLCAibmVnYXRpdmUiKSwKICAgICAgICAgICBkaXNlYXNlID0gZmFjdG9yKGRpc2Vhc2UsIGxldmVscyA9IHBsb3RfY29sb3JzJERpc2Vhc2UpKSAlPiUKICAgIGdncGxvdChhZXMoeSA9IGxhYmVsLCB4ID0gZGlzZWFzZSkpICsKICAgIGdlb21fdGlsZShhZXMoZmlsbCA9IGRpc2Vhc2UpLCAKICAgICAgICAgICAgICBjb2xvdXIgPSBteV9jZXRfcGFsWzFdLCBzaXplID0gMC40KSArCiAgICBzY2FsZV9maWxsX21hbnVhbCgiVHVtb3IgR3JvdXAiLCB2YWx1ZXMgPSBwbG90X2NvbG9ycyRDb2xvcikgKwogICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobnJvdyA9IDMsIGJ5cm93ID0gVFJVRSkpICsKICAgIHlsYWIoIiIpICsKICAgIHhsYWIoIiIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgYW5nbGUgPSA5MCwgaGp1c3QgPSAwKSwKICAgICAgICAgIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMCksCiAgICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArCiAgICBmYWNldF9ncmlkKGdyb3VwIH4gbWlybmEsIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIpCnAKCmdnc2F2ZSgiLi4vZmlndXJlcy9taXJuYV9pbW11bm9tb2Rfc3VwcG9ydGVkX2NvcnIucG5nIiwgcCwKICAgICAgIHdpZHRoID0gMTcsIGhlaWdodCA9IDUsIHVuaXRzID0gImNtIiwgZHBpID0gMzAwLCBzY2FsZSA9IDIpCmBgYAoKIyMjIEFsbCBjYXRlZ29yaWVzLi4uCgpgYGB7cn0KbWlybmFfYWxsX2NvbW1vbl9kZiA8LSBtaXJuYV9zdXBwb3J0X2RmICU+JQogICAgZ3JvdXBfYnkobWlybmEsIGRpc2Vhc2UpICU+JSAKICAgIHN1bW1hcml6ZShhbGxfc3VwcG9ydCA9IHN1bShtaXJiYXNlX3N1cHBvcnQpID4gMCAmCiAgICAgICAgICAgICAgICAgIHN1bShzeWduYWxfc3VwcG9ydCkgPiAwICYKICAgICAgICAgICAgICAgICAgc3VtKGltbXVuZV9zdXBwb3J0KSA+IDApICU+JSAKICAgIGZpbHRlcihhbGxfc3VwcG9ydCkgJT4lIAogICAgZ3JvdXBfYnkobWlybmEpICU+JQogICAgbXV0YXRlKG56X2Rpc2Vhc2UgPSBuX2Rpc3RpbmN0KGRpc2Vhc2UpKSAlPiUKICAgIGZpbHRlcihuel9kaXNlYXNlID4gMSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9Nn0KcCA8LSBtaXJuYV9zdXBwb3J0X2RmICU+JSAKICAgIGZpbHRlcihtaXJuYSAlaW4lIG1pcm5hX2FsbF9jb21tb25fZGYkbWlybmEsCiAgICAgICAgICAgY29ycmVsYXRlX3R5cGUgIT0gIm11dGF0aW9uIGxvYWQiKSAlPiUgCiAgICBtdXRhdGUoc3VwcG9ydCA9IChtaXJiYXNlX3N1cHBvcnQgfCBzeWduYWxfc3VwcG9ydCkgJiBpbW11bmVfc3VwcG9ydCwKICAgICAgICAgICBncm91cCA9IGZjdF9pbm9yZGVyKGdyb3VwKSwKICAgICAgICAgICBtaXJuYV9zaG9ydCA9IHN0cl9leHRyYWN0KG1pcm5hLCAibWlSLioiKSkgJT4lCiAgICBncm91cF9ieShtaXJuYSwgbGFiZWwpICU+JSAKICAgIG11dGF0ZShudW1fZGlzZWFzZXMgPSBuX2Rpc3RpbmN0KGRpc2Vhc2UpKSAlPiUgCiAgICBncm91cF9ieShncm91cCkgJT4lIAogICAgbXV0YXRlKGxhYmVsID0gZmN0X3Jlb3JkZXIobGFiZWwsIG51bV9kaXNlYXNlcykpICU+JSAKICAgIHVuZ3JvdXAoKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHkgPSBsYWJlbCwgeCA9IGRpc2Vhc2UpKSArCiAgICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBlc3RpbWF0ZSwgY29sb3VyID0gc3VwcG9ydCwgc2l6ZSA9IHN1cHBvcnQpKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50bigiQ29ycmVsYXRpb24iLCBjb2xvdXJzID0gbXlfY2V0X3BhbCkgKwogICAgc2NhbGVfY29sb3VyX21hbnVhbCgiU3VwcG9ydCIsIHZhbHVlcyA9IGMoIiMzMzMzMzMiLCAiI0U2OUYwMCIpKSArCiAgICBzY2FsZV9zaXplX21hbnVhbCgiU3VwcG9ydCIsIHZhbHVlcyA9IGMoMC4xLCAxKSkgKwogICAgeWxhYigiIikgKwogICAgeGxhYigiIikgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwKSwKICAgICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsKICAgIGZhY2V0X2dyaWQoZ3JvdXAgfiBtaXJuYV9zaG9ydCwgc2NhbGVzID0gImZyZWUiLCBzcGFjZSA9ICJmcmVlIikKcAoKZ2dzYXZlKCIuLi9maWd1cmVzL21pcjE0Ml9zdXBwb3J0ZWRfY29yci5wbmciLCBwLAogICAgICAgd2lkdGggPSAxNiwgaGVpZ2h0ID0gMTYsIHVuaXRzID0gImNtIiwgZHBpID0gMzAwLCBzY2FsZSA9IDEuOCkKYGBgCgo=